ifelse(!dir.exists(outdir), dir.create(outdir), FALSE)
[1] FALSE

Based on the results of my benchmark, I set out to align expression and accessibility profiles from the F74 developing thymus dataset to detect changes in accessibility along pseudotime trajectories. While the benchmark was based on the task of label propagation, I here use the two most faithful methods (Seurat CCA and Conos) to achieve a common embedding of ATAC-seq and RNA-seq cells.

Load datasets.

seu.rna@assays$RNA@data
33694 x 8321 sparse Matrix of class "dgCMatrix"
   [[ suppressing 68 column names ‘AAACCTGAGTTCGATC_1’, ‘AAACCTGCAAGTTGTC_1’, ‘AAACCTGCAATACGCT_1’ ... ]]
   [[ suppressing 68 column names ‘AAACCTGAGTTCGATC_1’, ‘AAACCTGCAAGTTGTC_1’, ‘AAACCTGCAATACGCT_1’ ... ]]
                                                                                                                                                            
RP11-34P13.3  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
FAM138A       . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
OR4F5         . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
RP11-34P13.7  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
RP11-34P13.8  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
RP11-34P13.14 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
RP11-34P13.9  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......

 ..............................
 ........suppressing 8253 columns and 33680 rows in show(); maybe adjust 'options(max.print= *, width = *)'
 ..............................
   [[ suppressing 68 column names ‘AAACCTGAGTTCGATC_1’, ‘AAACCTGCAAGTTGTC_1’, ‘AAACCTGCAATACGCT_1’ ... ]]
                                                                                                                                                         
AC023491.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
AC004556.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
AC233755.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
AC233755.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
AC240274.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
AC213203.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
FAM231B    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......

Filter genes with zero variance

rna.sce <- rna.sce[which(rna.gene.var > 0),]
atac.sce <- atac.sce[which(atac.gene.var > 0),]

rna.sce; atac.sce
class: SingleCellExperiment 
dim: 24510 8321 
metadata(0):
assays(3): counts cpm logcounts
rownames(24510): RP11-34P13.3 RP11-34P13.7 ... AC233755.1 AC240274.1
rowData names(0):
colnames(8321): AAACCTGAGTTCGATC_1 AAACCTGCAAGTTGTC_1 ... TTTGTCAAGCTGAACG_2 TTTGTCAGTATTAGCC_2
colData names(1): annotation
reducedDimNames(0):
spikeNames(0):
class: SingleCellExperiment 
dim: 31122 5793 
metadata(0):
assays(3): counts cpm logcounts
rownames(31122): A1BG A1BG-AS1 ... ZYX ZZEF1
rowData names(0):
colnames(5793): AAACGAAAGTGAACCG-1 AAACGAACATCGGCCA-1 ... TTTGTGTTCGATCGCG-1 TTTGTGTTCTGAGTAC-1
colData names(28): orig.ident nCount_ATAC ... nFeature_ACTIVITY ident
reducedDimNames(2): LSI UMAP
spikeNames(0):

Integration of T cells clusters

I re-run the integration based on the T cell subset. To select cells from the scATAC dataset, I take the SnapATAC clusters that best correspond to T-cells, based on label transfer.

tcells.sce.atac <- atac.sce[,which(as.numeric(atac.sce$seurat_clusters) %in% c(1:9))]

tcells.rna.ix <- which(rna.sce$annotation %in% c("DN","DP (Q)", "DP (P)", "SP (1)", "SP (2)"))
tcells.sce.rna <- rna.sce[,tcells.rna.ix]

tcells.sce.list <- list(RNA=tcells.sce.rna, ATAC=tcells.sce.atac)

## Make color palette 4 cell types
cell.types <- as.character(unique(tcells.sce.rna$annotation))
cell.type.pal <- brewer.pal(length(cell.types), "Set1") %>% rev() %>% setNames(cell.types)

Next, I select genes on which to perform integration. I take the union of the most variable features in the RNA dataset and the most covered features in the ATAC dataset

Remove cell cycle genes, that might interfere with pseudotime ordering

cell_cycle_genes <- read.table("~/annotations/cell_cycle_genes.tsv")$V1

integrate_features_union <- union(hvg.rna, hcg.atac)
integrate_features_union <- setdiff(integrate_features_union, cell_cycle_genes) 

## Select features in both datasets
integrate_features_union <- intersect(integrate_features_union, intersect(rownames(tcells.sce.list$ATAC), rownames(tcells.sce.list$RNA))) 

Visualize T cells in RNA dataset

tcells.seu.list <- map(tcells.sce.list, ~ as.Seurat(.x))
All keys should be one or more alphanumeric characters followed by an underscore '_', setting key to LSI_All keys should be one or more alphanumeric characters followed by an underscore '_', setting key to UMAP_
tcells.RNA.union <- tcells.seu.list$RNA
VariableFeatures(tcells.RNA.union) <- integrate_features_union
tcells.RNA.union <- ScaleData(tcells.RNA.union) %>% RunPCA() %>% RunUMAP(dims=1:40)
Centering and scaling data matrix

  |                                                                                  
  |                                                                            |   0%
  |                                                                                  
  |===============                                                             |  20%
  |                                                                                  
  |==============================                                              |  40%
  |                                                                                  
  |==============================================                              |  60%
  |                                                                                  
  |=============================================================               |  80%
  |                                                                                  
  |============================================================================| 100%
The following 176 features requested have zero variance (running reduction without them): TM4SF18, MEOX2, ASGR2, DMRT2, KIR2DL4, CACNA1B, FREM2, MYZAP, TOX3, PRIMA1, PPP2R2B, PTPRZ1, CMTM5, TREM2, CALB2, TMTC1, CDH6, PKHD1L1, ISLR2, KRT7, MYH8, DCX, CCSER1, MSLN, CDH19, EYA4, P2RY12, PLA2G2D, ADAMTS16, KCNJ5, PAPPA, QRFPR, ADGRG2, NFIA-AS2, LUZP2, NALCN, FABP7, TSPEAR-AS1, GPR1, CD300LF, A4GALT, AP000439.1, CEACAM3, SLITRK2, KIR3DL1, AC147651.1, MUC15, FFAR4, C11orf53, KLK11, WIF1, CFHR1, TMEM233, FOLR3, GRID2, GALNT15, FAM19A1, TAC1, PTCHD4, CCL11, ZNF385B, GABRA1, ADH1B, LINC01048, LMOD1, TNNT1, ACTL6B, SEMA3E, HOXD-AS2, NTF4, SLC35F1, MANEAL, FEV, RAB3C, SYT9, HMCN2, ZFHX4-AS1, PCSK9, MMP12, ABCB11, AC002066.1, ADAM23, ADCY5, ADGB, ADGRB1, ANKRD29, ATP2B2, C4orf45, CCDC33, CDH18, CDK15, CERS1, CFAP161, CLRN1-AS1, CNBD1, CNGB3, CNTN5, COL8A2, CTNND2, DCC, DSCAM, EPHA6, FGF12, FSTL5, GABBR2, GABRB1, GABRG3, GALNT13, GHR, GLIS3, GNG12-AS1, GNGT1, GRIN2B, GRM1, GRM8, HMCN1, HPSE2, HS3ST5, HS6ST3, IL1RAPL2, ITGBL1, KCNB1, KCNH7, LINC00639, LINC01169, LINC01170, LINC01182, LINC01317, LINC01505, LINGO1, LINGO2, LRRC4C, LRRTM4, LY75-CD302, MAP6, MLIP, MYRIP, NEBL, NKAIN2, NKAIN3, NRG1, OPCML, PCDH15, PCDHGA4, PDZRN4, PIK3C2G, PKNOX2, PNPLA1, POU6F2, PPFIA2, PRR5-ARHGAP8, PTPN5, PTPRT, RALYL, RERG, RGS7, RIMS1, RIN2, RYR3, SHANK2, SNTG1, SORCS3, STON1-GTF2A1L, SYN2, TENM2, THSD4, TRPC6, TSPEAR, TVP23C-CDRT4, UNC80, USH1C, VAX2, VWA3B, XKR4, ZBTB7C, ZNF804BPC_ 1 
Positive:  SATB1, PTPRC, MTSS1, APBB1IP, LYST, SH2D1A, CAMK4, LBH, FBLN5, LEF1 
       PLEKHG1, VOPP1, CD247, MXD1, TCF12, ARHGEF7, ALDH1A2, NFATC3, TBC1D19, SYTL3 
       ADAMTS17, ANO6, DAPK1, CALN1, THEMIS, PITPNM2, NLGN4X, MAPRE2, GALNT7, ZNF280D 
Negative:  ENO1, GSTP1, FABP5, TMSB10, SMS, PKM, NME4, VIM, RPL37A, YWHAQ 
       NCL, IGFBP2, CAPG, NDUFA12, TRDC, PGK1, PARVB, LDHB, ATOX1, SELL 
       CDC123, NUDC, IGLL1, UBE2N, FXYD2, GMPS, C20orf27, SLC25A39, ANXA1, C12orf75 
PC_ 2 
Positive:  RPL37A, EIF3H, LDHB, TBCA, IL32, SERGEF, SMPD3, ITGAE, AATF, FBLN5 
       NDUFA12, SOD1, DNAJC15, C12orf75, MRPL33, TMSB10, HNRNPC, CCDC57, SMCO4, GDI2 
       OLA1, ALDH1A2, COX7A2L, CST3, CYSTM1, ATOX1, GYPC, PDCD6, FABP5, RALY 
Negative:  MBNL1, ITPR2, PTPRC, MTRNR2L12, ADAM10, MSI2, CDK6, SELL, JCHAIN, RNF213 
       MME, TCF12, BPTF, BCL11B, NIPBL, MACF1, PIK3R1, HIVEP3, SOCS2, GALNT7 
       IKZF2, RUNX1, SPTBN1, PRRC2B, BCL2, DIAPH1, MYCBP2, SLC38A1, MBP, RUFY3 
PC_ 3 
Positive:  HLA-B, TOX2, COTL1, CTSW, KLRB1, CD40LG, SIRPG, GZMM, CLDN1, CD74 
       ITM2A, GBP2, BACH2, HPGD, PDE4D, TNFRSF1B, S100A10, CLEC2D, XCL1, GFOD1 
       CRTAM, MATK, DENND2D, PDCD1, GIMAP4, STAT1, ZNF683, CD226, HLA-A, TNFRSF25 
Negative:  TFDP2, JCHAIN, ATP6AP1L, DEFA6, MME, GALNT2, PCGF5, ADGRG1, GLIPR1, MSI2 
       NINL, CEP70, CDK6, PTPN2, FXYD2, TRDC, GSTP1, SELL, SOCS2, RGPD3 
       SMPD3, FABP5, LYST, SSBP2, PITPNM2, DLEU7, UBE2E1, NUCB2, PPP1R1C, BCL11A 
PC_ 4 
Positive:  SMPD3, C12orf75, TUBA1C, LCP1, HIST1H2AB, SMS, SMCO4, GMPS, FABP5, XPO1 
       IGFBP2, CCND3, TAF15, RGS3, SREBF2, YWHAQ, TMSB15A, ABCD3, NCL, PHIP 
       GNAS, EPB41L2, SYNE2, STAG2, CNTLN, VIM, NUP210, MCTP1, NUDC, NFATC3 
Negative:  JCHAIN, SELL, DEFA6, SOCS2, GLIPR1, ATP6AP1L, MME, RGPD3, XG, DPP4 
       IFI6, ENAM, GNAL, EVL, NEGR1, FRMPD2, EVA1A, PIK3CD, GALNT2, NDST3 
       MBP, RNF144A, FXYD2, COL1A2, SMIM24, NDFIP2, OXNAD1, ACTN1, LSP1, LINC00861 
PC_ 5 
Positive:  HPGD, BACH2, TOX2, ITM2A, GZMM, ST6GAL1, ARAP2, CD96, TGFBR2, LZTFL1 
       VIM, EVL, CD44, SATB1, IL2, TUSC3, CYTIP, PDE4D, MAD1L1, SLC2A3 
       ARHGAP31, GPR183, PGK1, ANKRD44, STK4, BCL2, GNG2, IKZF1, ICOS, ETS1 
Negative:  XCL1, TNFRSF9, CTSW, XCL2, GBP2, TNFRSF1B, S100A4, GNG4, NPW, CD74 
       HOPX, IGFBP4, CD151, SH3BGRL2, NFATC1, LYST, ATP9A, NR4A2, LINC01480, CLEC2D 
       MIR3142HG, LINC01281, PREX1, HDAC9, PHACTR1, MAP7, CST3, CXCR3, PITPNM2, PDCD1 
09:42:53 UMAP embedding parameters a = 0.9922 b = 1.112
09:42:53 Read 7101 rows and found 40 numeric columns
09:42:53 Using Annoy for neighbor search, n_neighbors = 30
09:42:53 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:42:55 Writing NN index file to temp file /tmp/RtmpInlUFP/file4d1e59aaa998
09:42:55 Searching Annoy index using 1 thread, search_k = 3000
09:42:57 Annoy recall = 100%
09:42:59 Commencing smooth kNN distance calibration using 1 thread
09:43:03 Initializing from normalized Laplacian + noise
09:43:03 Commencing optimization for 500 epochs, with 314138 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:43:22 Optimization finished
DimPlot(tcells.RNA.union, group.by = "annotation", label=TRUE) + ggtitle("RNA - feature union")

Visualize markers

t.cell.markers <- list(known.markers = c("CD34", "IGLL1", "TRGC2", "TRDC", "PTCRA", "TRBC2", "TRAC", "CD4", "CD8A", "CD8B"),
                       chemokine.receptors = c("CCR9", "CCR7"),
                       tcr.activation = c("CD5", "CD27"),
                       proliferation=c("PCNA", "CDK1", "MKI67"),
                       cyclin.D = c("CCND2", "CCND3"),
                       recombination=c("RAG1", "RAG2"),
                       apoptosis=c("HRK","BMF", "TP53INP1"),
                       stage.markers = c("ST18", "HIVEP3", "RGPD3", "SMPD3", "AQP3", "RORC", "SATB1", "TOX2")
                       ) 
# FeaturePlot(tcells.RNA.ref, features = t.cell.markers$known.markers, cols = viridis::viridis(n=10))
FeaturePlot(tcells.RNA.union, features = t.cell.markers$known.markers, cols = viridis::viridis(n=10))

Visualize T cells in ATAC dataset: is the trajectory visible in the binary matrix?

tcells.ATAC.union <- tcells.seu.list$ATAC
# tcells.ATAC.union <- NormalizeData(tcells.ATAC.union)
VariableFeatures(tcells.ATAC.union) <- integrate_features_union
tcells.ATAC.union <- RunLSI(tcells.ATAC.union, n=50, scale.max = NULL)
RunLSI is being moved to Signac. Equivalent functionality can be achieved via the Signac::RunTFIDF and Signac::RunSVD functions; for more information on Signac, please see https://github.com/timoast/SignacRunLSI is being moved to Signac. Equivalent functionality can be achieved via the Signac::RunTFIDF and Signac::RunSVD functions; for more information on Signac, please see https://github.com/timoast/SignacRunLSI is being moved to Signac. Equivalent functionality can be achieved via the Signac::RunTFIDF and Signac::RunSVD functions; for more information on Signac, please see https://github.com/timoast/SignacPerforming TF-IDF normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Running SVD on TF-IDF matrix
Scaling cell embeddings
Cannot add objects with duplicate keys (offending key: LSI_), setting key to 'lsi_'
tcells.ATAC.union <- RunUMAP(tcells.ATAC.union, reduction = "lsi", dims = 1:50)
09:43:59 UMAP embedding parameters a = 0.9922 b = 1.112
09:43:59 Read 4977 rows and found 50 numeric columns
09:43:59 Using Annoy for neighbor search, n_neighbors = 30
09:43:59 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:44:00 Writing NN index file to temp file /tmp/RtmpInlUFP/file4d1e64c0f0d8
09:44:00 Searching Annoy index using 1 thread, search_k = 3000
09:44:02 Annoy recall = 100%
09:44:04 Commencing smooth kNN distance calibration using 1 thread
09:44:08 Initializing from normalized Laplacian + noise
09:44:08 Commencing optimization for 500 epochs, with 172060 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:44:21 Optimization finished
Cannot add objects with duplicate keys (offending key: UMAP_), setting key to 'umap_'
DimPlot(tcells.ATAC.union, reduction = "umap", group.by = "seurat_clusters", label = TRUE) + ggtitle("ATAC gmat")

Run CCA and Conos

Run CCA

sce.list <- tcells.sce.list
reference = "RNA"
query = "ATAC" 
seurat.list <- imap(sce.list, ~ as.Seurat(.x, assay=.y))
All keys should be one or more alphanumeric characters followed by an underscore '_', setting key to LSI_All keys should be one or more alphanumeric characters followed by an underscore '_', setting key to UMAP_
seurat.list <- imap(seurat.list, ~ RenameCells(.x, add.cell.id=.y))
## Scale data
seurat.list <- map(seurat.list, ~ ScaleData(.x))
Centering and scaling data matrix

  |                                                                                                                                                     
  |                                                                                                                                               |   0%
  |                                                                                                                                                     
  |======                                                                                                                                         |   4%
  |                                                                                                                                                     
  |===========                                                                                                                                    |   8%
  |                                                                                                                                                     
  |=================                                                                                                                              |  12%
  |                                                                                                                                                     
  |=======================                                                                                                                        |  16%
  |                                                                                                                                                     
  |=============================                                                                                                                  |  20%
  |                                                                                                                                                     
  |==================================                                                                                                             |  24%
  |                                                                                                                                                     
  |========================================                                                                                                       |  28%
  |                                                                                                                                                     
  |==============================================                                                                                                 |  32%
  |                                                                                                                                                     
  |===================================================                                                                                            |  36%
  |                                                                                                                                                     
  |=========================================================                                                                                      |  40%
  |                                                                                                                                                     
  |===============================================================                                                                                |  44%
  |                                                                                                                                                     
  |=====================================================================                                                                          |  48%
  |                                                                                                                                                     
  |==========================================================================                                                                     |  52%
  |                                                                                                                                                     
  |================================================================================                                                               |  56%
  |                                                                                                                                                     
  |======================================================================================                                                         |  60%
  |                                                                                                                                                     
  |============================================================================================                                                   |  64%
  |                                                                                                                                                     
  |=================================================================================================                                              |  68%
  |                                                                                                                                                     
  |=======================================================================================================                                        |  72%
  |                                                                                                                                                     
  |=============================================================================================================                                  |  76%
  |                                                                                                                                                     
  |==================================================================================================================                             |  80%
  |                                                                                                                                                     
  |========================================================================================================================                       |  84%
  |                                                                                                                                                     
  |==============================================================================================================================                 |  88%
  |                                                                                                                                                     
  |====================================================================================================================================           |  92%
  |                                                                                                                                                     
  |=========================================================================================================================================      |  96%
  |                                                                                                                                                     
  |===============================================================================================================================================| 100%
Centering and scaling data matrix

  |                                                                                                                                                     
  |                                                                                                                                               |   0%
  |                                                                                                                                                     
  |====                                                                                                                                           |   3%
  |                                                                                                                                                     
  |=========                                                                                                                                      |   6%
  |                                                                                                                                                     
  |=============                                                                                                                                  |   9%
  |                                                                                                                                                     
  |==================                                                                                                                             |  12%
  |                                                                                                                                                     
  |======================                                                                                                                         |  16%
  |                                                                                                                                                     
  |===========================                                                                                                                    |  19%
  |                                                                                                                                                     
  |===============================                                                                                                                |  22%
  |                                                                                                                                                     
  |====================================                                                                                                           |  25%
  |                                                                                                                                                     
  |========================================                                                                                                       |  28%
  |                                                                                                                                                     
  |=============================================                                                                                                  |  31%
  |                                                                                                                                                     
  |=================================================                                                                                              |  34%
  |                                                                                                                                                     
  |======================================================                                                                                         |  38%
  |                                                                                                                                                     
  |==========================================================                                                                                     |  41%
  |                                                                                                                                                     
  |===============================================================                                                                                |  44%
  |                                                                                                                                                     
  |===================================================================                                                                            |  47%
  |                                                                                                                                                     
  |========================================================================                                                                       |  50%
  |                                                                                                                                                     
  |============================================================================                                                                   |  53%
  |                                                                                                                                                     
  |================================================================================                                                               |  56%
  |                                                                                                                                                     
  |=====================================================================================                                                          |  59%
  |                                                                                                                                                     
  |=========================================================================================                                                      |  62%
  |                                                                                                                                                     
  |==============================================================================================                                                 |  66%
  |                                                                                                                                                     
  |==================================================================================================                                             |  69%
  |                                                                                                                                                     
  |=======================================================================================================                                        |  72%
  |                                                                                                                                                     
  |===========================================================================================================                                    |  75%
  |                                                                                                                                                     
  |================================================================================================================                               |  78%
  |                                                                                                                                                     
  |====================================================================================================================                           |  81%
  |                                                                                                                                                     
  |=========================================================================================================================                      |  84%
  |                                                                                                                                                     
  |=============================================================================================================================                  |  88%
  |                                                                                                                                                     
  |==================================================================================================================================             |  91%
  |                                                                                                                                                     
  |======================================================================================================================================         |  94%
  |                                                                                                                                                     
  |===========================================================================================================================================    |  97%
  |                                                                                                                                                     
  |===============================================================================================================================================| 100%
## Calculate CCA anchors
transfer.anchors <- FindTransferAnchors(reference = seurat.list[[reference]], 
                                        query = seurat.list[[query]],
                                        features = integrate_features_union, 
                                        reduction = "cca")
Running CCA on different assaysRunning CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 19433 anchors
Filtering anchors
    Retained 1616 anchors
Extracting within-dataset neighbors
## Impute expression profiles for ATAC cells (for all genes, not just integration features)
refdata <- GetAssayData(seurat.list$RNA, assay = "RNA", slot = "data")
imputation <- TransferData(anchorset = transfer.anchors, refdata = refdata, weight.reduction = seurat.list$ATAC[["LSI"]])
Finding integration vectors
Finding integration vector weights
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Transfering 24510 features onto reference data
## Merge datasets and co-embed
seurat.list$ATAC[["RNA"]] <- imputation
coembed <- merge(x = seurat.list$RNA, y = seurat.list$ATAC)

coembed <- ScaleData(coembed, features = integrate_features_union, do.scale = FALSE)
Centering data matrix

  |                                                                                                                                                     
  |                                                                                                                                               |   0%
  |                                                                                                                                                     
  |=============================                                                                                                                  |  20%
  |                                                                                                                                                     
  |=========================================================                                                                                      |  40%
  |                                                                                                                                                     
  |======================================================================================                                                         |  60%
  |                                                                                                                                                     
  |==================================================================================================================                             |  80%
  |                                                                                                                                                     
  |===============================================================================================================================================| 100%
coembed <- RunPCA(coembed, features = integrate_features_union, verbose = FALSE)
coembed <- RunUMAP(coembed, dims = 1:30)
09:52:05 UMAP embedding parameters a = 0.9922 b = 1.112
09:52:05 Read 12078 rows and found 30 numeric columns
09:52:05 Using Annoy for neighbor search, n_neighbors = 30
09:52:05 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:52:07 Writing NN index file to temp file /tmp/RtmpInlUFP/file4d1e3a771214
09:52:07 Searching Annoy index using 1 thread, search_k = 3000
09:52:12 Annoy recall = 100%
09:52:15 Commencing smooth kNN distance calibration using 1 thread
09:52:19 Initializing from normalized Laplacian + noise
09:52:19 Commencing optimization for 200 epochs, with 559810 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:52:33 Optimization finished
coembed <- AddMetaData(coembed, metadata = ifelse(colnames(coembed) %in% colnames(seurat.list[[reference]]), reference, query), col.name = "tech")

DimPlot(coembed, group.by = c('tech', "annotation"))

FeaturePlot(coembed, features = t.cell.markers$stage.markers, split.by = "tech", cols = viridis::viridis(n=100)) + ggtitle("Stage Markers")

FeaturePlot(coembed, features = t.cell.markers$known.markers, split.by = "tech", slot = "data", cols = viridis::viridis(n=100))

FeaturePlot(coembed, features = t.cell.markers$recombination, split.by = "tech", slot = "data", cols = viridis::viridis(n=100)) 

Transfer labels on ATAC dataset

FeaturePlot(coembed, features = "prediction.score.max", cells = which(coembed$tech=="ATAC")) + scale_color_viridis_c()
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

Run Pseudotime analysis

Identify cell of origin based on IGLL1 and CD34

FeaturePlot(coembed, features = c("IGLL1", "CD34"), split.by = "tech", slot = "data", cols = viridis::viridis(n=100))

cell.oo <-
  coembed@meta.data %>% 
  rownames_to_column("cell") %>%
  mutate(IGLL1=coembed@assays$RNA@counts["IGLL1",cell]) %>%
  select(cell, annotation, IGLL1) %>%
  arrange(-IGLL1) %>%
  filter(annotation=="DN") %>%
  top_n(1, IGLL1) %>%
  pull(cell)

coembed@reductions$umap@cell.embeddings %>%
  as.tibble(rownames="cell") %>%
  mutate(cell.oo = ifelse(cell %in% cell.oo, T, F)) %>%
  ggplot(aes(UMAP_1, UMAP_2)) +
  geom_point(color="grey50") +
  geom_point(data=. %>% filter(cell.oo),color='red') +
  ggrepel::geom_text_repel(data=. %>% filter(cell.oo), aes(label="cell of origin"), color='red') +
  theme_cowplot() 
`as.tibble()` is deprecated, use `as_tibble()` (but mind the new semantics).
This warning is displayed once per session.

coembed <- AddMetaData(coembed, ifelse(colnames(coembed)==cell.oo, TRUE, FALSE), col.name = "iroot_cell")

  
merged.sce <- SingleCellExperiment(list(counts=coembed@assays$RNA@counts, logcounts=coembed@assays$RNA@data), colData=coembed@meta.data[, c("annotation", "tech", "iroot_cell")],
                     reducedDims = map(coembed@reductions, ~ .x@cell.embeddings))

saveRDS(object = merged.sce, "~/my_data/Tcells_CCA_integration_20191203.RDS")
saveRDS(object = integrate_features_union, "~/my_data/intFeatures_Tcells_CCA_integration_20191203.RDS")

I infer pseudotime using the diffusion pseudotime algorithm as implemented in scanpy. Making an R/reticulate wrapper for this function would be nice, but for now, see multiOmic_benchmark/DPT_tcells.ipynb.

Read scanpy output and save in R object.

dpt <- read.csv('~/my_data/Tcells_CCA_integration_20191127_scanpy_dpt.csv') %>%
  select(X, dpt_pseudotime)

coembed <- AddMetaData(coembed, column_to_rownames(dpt, 'X'))
saveRDS(coembed, "~/my_data/Tcells_CCA_integration_seurat_20191127.Rmd")
coembed <- readRDS("~/my_data/Tcells_CCA_integration_seurat_20191127.Rmd")

Visualize pseudotime

FeaturePlot(coembed, reduction = "umap", feature = "dpt_pseudotime", split.by = "tech", col=viridis::viridis(10)) 

Save figure

coembed.umaps.pl <- plot_grid(
  DimPlot(coembed, group.by = c("tech")) + theme(legend.position = "top"),
  DimPlot(coembed, group.by = c("annotation"), cols = cell.type.pal, label = TRUE, label.size = 5) + theme(legend.position = "none"),
  FeaturePlot(coembed, reduction = "umap", feature = "dpt_pseudotime") + scale_color_viridis_c(name="Diffusion\npseudotime") + ggtitle(""),
  nrow=1, ncol=3, rel_widths = c(1,1,1.2),
  labels = c("A", "B", "C")
) 
Using `as.character()` on a quosure is deprecated as of rlang 0.3.0.
Please use `as_label()` or `as_name()` instead.
This warning is displayed once per session.Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
coembed.umaps.pl +
  ggsave(paste0(outdir, "coembed_umaps.png"), width=12, height = 4)

coembed@meta.data %>%
  dplyr::mutate(`DPT rank`=dense_rank(dpt_pseudotime)) %>%
  ggplot(aes(dpt_pseudotime)) +
  geom_histogram(aes(fill=annotation), bins=50) +
  facet_grid(annotation~tech, scales="free_y") +
  theme_bw(base_size = 16) +
  scale_fill_manual(values = cell.type.pal)

Check expression of markers along pseudotime

Bin pseudotime and visualize cell type composition

dpt.df <- 
  coembed@meta.data %>%
  rownames_to_column("cell") %>%
  dplyr::mutate(dpt_rank=dense_rank(dpt_pseudotime)) %>%
  mutate(dpt_bin=cut(dpt_rank, breaks = 100)) %>%
  mutate(dpt_bin=as.numeric(dpt_bin)) %>%
  select(cell,tech, annotation, prediction.score.max, dpt_bin, dpt_pseudotime, dpt_rank)

cell.type.pl <- dpt.df %>%
  ggplot(aes(dpt_bin, fill = annotation)) +
  # geom_histogram(bins=100) +
  geom_bar() +
  scale_fill_manual(values=cell.type.pal, na.value="grey50") +
  facet_grid(tech~., scales="free_y") +
  xlab("Pseudotime bin") +
  theme_bw(base_size = 16)

cell.type.pl

Correlation between global accessibility and pseudotime ordering.

Motif analysis

I initially wanted to call peaks from SnapATAC clusters, then build a cell x peak matrix on those detected peaks, but SnapATAC/MACS2 were drivin me nuts.

Alternative: load peak matrix from cellranger and add to snap object

snap.pmat <- createSnapFromPmat(mat=t(filt.peaks[,snap.out@barcode]), barcodes=snap.out@barcode, peaks=peaks.gr)
snap.pmat
number of barcodes: 5793
number of bins: 0
number of genes: 0
number of peaks: 93607
number of motifs: 0

Calculating deviations in TF accessibility using ChromVAR. This is a measure of how much is motif accessibility in each cell is enriched compared to all the cells and general cell coverage. While SnapATAC has an wrapper around ChromVAR that outputs the deviation matrix, I just take the code from that function and run every step separately to keep the useful outputs and statistics of chromVAR.

snap.pmat = makeBinary(snap.pmat, "pmat")

obj = snap.pmat
input.mat="pmat"
min.count=10
species="Homo sapiens"
genome=BSgenome.Hsapiens.UCSC.hg38

data.use = obj@pmat
peak.use = obj@peak

ncell = nrow(data.use)

idy = which(Matrix::colSums(data.use) >= min.count)
data.use = data.use[,idy,dropping=TRUE]
    
peak.use = peak.use[idy]

rse <- SummarizedExperiment(
        assays = list(counts = t(data.use)), 
                 rowRanges = peak.use, 
                 colData = DataFrame(Cell_Type=1:nrow(data.use), depth=Matrix::rowSums(data.use))
    );
rse <- addGCBias(rse, genome = genome);
motifs <- getJasparMotifs(collection = "CORE", species=species);
motif_mm <- matchMotifs(motifs, rse, genome = genome);
dev <- computeDeviations(object = rse, annotations = motif_mm);
'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading
var <- computeVariability(dev)
'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading
plotVariability(var, use_plotly = F, n = 8)

Save

rowData(dev) %<>%
  as.tibble(rownames="motif") %>%
  full_join(var) %>%
  column_to_rownames('motif') %>%
  DataFrame()
Joining, by = "name"
Column `name` joining character vector and factor, coercing into character vector

Visualize deviation scores of the most variable motifs, ordered in pseudotime.

Compare motif accessibility trend with gene expression trend along pseudotime.

map(list("JUN", "ELK3", "RUNX2", "REL", "FOS", "ETV6"), ~ plot.tfs(.x) + ggsave(paste0(outdir, paste0('TF_plot_',.x,".png")), width = 8, height=5))
Unequal factor levels: coercing to characterbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding factor and character vector, coercing into character vectorbinding character and factor vector, coercing into character vector
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]

–>

–> –> –> –> –> –> –> –>

–> –> –> –> –> –> –>

–> –> –> –> –>

–> –> –> –>

–> –> –>


LS0tCnRpdGxlOiAiUHNldWRvdGltZSBhbmFseXNpcyBvZiBULWNlbGxzIGluIGRldmVsb3BpbmcgdGh5bXVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoY29ub3MpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeShjaHJvbVZBUikKbGlicmFyeShtb3RpZm1hdGNocikKbGlicmFyeShCU2dlbm9tZS5Ic2FwaWVucy5VQ1NDLmhnMzgpCiMgbGlicmFyeShtb25vY2xlMykKc291cmNlKCJ+L211bHRpT21pY19iZW5jaG1hcmsvdXRpbHMuUiIpCnNvdXJjZSgifi9tdWx0aU9taWNfYmVuY2htYXJrL2ludGVncmF0ZUJlbmNobWFyay5SIikKc291cmNlKCJ+L211bHRpT21pY19iZW5jaG1hcmsvcHJlcHJvY2Vzcy9zZWxlY3RGZWF0dXJlcy5SIikKCiMjIE1ha2Ugb3V0cHV0IGRpcmVjdG9yeQpvdXRkaXIgPC0gIn4vbXVsdGlPbWljX2JlbmNobWFyay9yZXBvcnQvb3V0cHV0LzIwMTkxMTI3X3RjZWxsVHJhamVjdG9yeS8iCmlmZWxzZSghZGlyLmV4aXN0cyhvdXRkaXIpLCBkaXIuY3JlYXRlKG91dGRpciksIEZBTFNFKQpgYGAKCkJhc2VkIG9uIHRoZSByZXN1bHRzIG9mIG15IGJlbmNobWFyaywgSSBzZXQgb3V0IHRvIGFsaWduIGV4cHJlc3Npb24gYW5kIGFjY2Vzc2liaWxpdHkgcHJvZmlsZXMgZnJvbSB0aGUgRjc0IGRldmVsb3BpbmcgdGh5bXVzIGRhdGFzZXQgdG8gZGV0ZWN0IGNoYW5nZXMgaW4gYWNjZXNzaWJpbGl0eSBhbG9uZyBwc2V1ZG90aW1lIHRyYWplY3Rvcmllcy4gV2hpbGUgdGhlIGJlbmNobWFyayB3YXMgYmFzZWQgb24gdGhlIHRhc2sgb2YgbGFiZWwgcHJvcGFnYXRpb24sIEkgaGVyZSB1c2UgdGhlIHR3byBtb3N0IGZhaXRoZnVsIG1ldGhvZHMgKFNldXJhdCBDQ0EgYW5kIENvbm9zKSB0byBhY2hpZXZlIGEgY29tbW9uIGVtYmVkZGluZyBvZiBBVEFDLXNlcSBhbmQgUk5BLXNlcSBjZWxscy4KCkxvYWQgZGF0YXNldHMuCgpgYGB7cn0Kcm5hLnNjZSA8LSByZWFkUkRTKCJ+L215X2RhdGEvRjc0X1JOQV9zZXVyYXRfcHJvY2Vzc2VkLlJEUyIpCmF0YWMuc2NlIDwtIHJlYWRSRFMoIn4vbXlfZGF0YS9GNzRfQVRBQ19zbmFwQXRhY19wcm9jZXNzZWRfYmdtYXQuUkRTIikKCiMjIFJlLW5vcm1hbGl6ZSBSTkEgZGF0YQpzZXUucm5hIDwtIGFzLlNldXJhdChybmEuc2NlLCBjb3VudHMgPSAiY291bnRzIikKc2V1LnJuYSA8LSBOb3JtYWxpemVEYXRhKHNldS5ybmEpCmxvZ2NvdW50cyhybmEuc2NlKSA8LSBzZXUucm5hQGFzc2F5cyRSTkFAZGF0YQoKYGBgCgpGaWx0ZXIgZ2VuZXMgd2l0aCB6ZXJvIHZhcmlhbmNlCmBgYHtyfQpybmEuZ2VuZS52YXIgPC0gYXMubWF0cml4KGNvdW50cyhybmEuc2NlKSkgJT4lIHJvd1ZhcnMoKQphdGFjLmdlbmUudmFyIDwtIGFzLm1hdHJpeChjb3VudHMoYXRhYy5zY2UpKSAlPiUgcm93VmFycygpCgpybmEuc2NlIDwtIHJuYS5zY2Vbd2hpY2gocm5hLmdlbmUudmFyID4gMCksXQphdGFjLnNjZSA8LSBhdGFjLnNjZVt3aGljaChhdGFjLmdlbmUudmFyID4gMCksXQoKcm5hLnNjZTsgYXRhYy5zY2UKYGBgCgoKIyMgSW50ZWdyYXRpb24gb2YgVCBjZWxscyBjbHVzdGVycwpJIHJlLXJ1biB0aGUgaW50ZWdyYXRpb24gYmFzZWQgb24gdGhlIFQgY2VsbCBzdWJzZXQuIFRvIHNlbGVjdCBjZWxscyBmcm9tIHRoZSBzY0FUQUMgZGF0YXNldCwgSSB0YWtlIHRoZSBTbmFwQVRBQyBjbHVzdGVycyB0aGF0IGJlc3QgY29ycmVzcG9uZCB0byBULWNlbGxzLCBiYXNlZCBvbiBsYWJlbCB0cmFuc2Zlci4KCmBgYHtyfQp0Y2VsbHMuc2NlLmF0YWMgPC0gYXRhYy5zY2VbLHdoaWNoKGFzLm51bWVyaWMoYXRhYy5zY2Ukc2V1cmF0X2NsdXN0ZXJzKSAlaW4lIGMoMTo5KSldCgp0Y2VsbHMucm5hLml4IDwtIHdoaWNoKHJuYS5zY2UkYW5ub3RhdGlvbiAlaW4lIGMoIkROIiwiRFAgKFEpIiwgIkRQIChQKSIsICJTUCAoMSkiLCAiU1AgKDIpIikpCnRjZWxscy5zY2Uucm5hIDwtIHJuYS5zY2VbLHRjZWxscy5ybmEuaXhdCgp0Y2VsbHMuc2NlLmxpc3QgPC0gbGlzdChSTkE9dGNlbGxzLnNjZS5ybmEsIEFUQUM9dGNlbGxzLnNjZS5hdGFjKQoKIyMgTWFrZSBjb2xvciBwYWxldHRlIDQgY2VsbCB0eXBlcwpjZWxsLnR5cGVzIDwtIGFzLmNoYXJhY3Rlcih1bmlxdWUodGNlbGxzLnNjZS5ybmEkYW5ub3RhdGlvbikpCmNlbGwudHlwZS5wYWwgPC0gYnJld2VyLnBhbChsZW5ndGgoY2VsbC50eXBlcyksICJTZXQxIikgJT4lIHJldigpICU+JSBzZXROYW1lcyhjZWxsLnR5cGVzKQpgYGAKCk5leHQsIEkgc2VsZWN0IGdlbmVzIG9uIHdoaWNoIHRvIHBlcmZvcm0gaW50ZWdyYXRpb24uIEkgdGFrZSB0aGUgdW5pb24gb2YgdGhlIG1vc3QgdmFyaWFibGUgZmVhdHVyZXMgaW4gdGhlIFJOQSBkYXRhc2V0IGFuZCB0aGUgbW9zdCBjb3ZlcmVkIGZlYXR1cmVzIGluIHRoZSBBVEFDIGRhdGFzZXQKCmBgYHtyfQpoY2cuYXRhYyA8LSBzZWxlY3RfaGlnaGx5Q292ZXJlZCh0Y2VsbHMuc2NlLmxpc3QkQVRBQywgZnJhY19jZWxscyA9IDAuMikKaHZnLnJuYSA8LSBzZWxlY3RfaGlnaGx5VmFyaWFibGUodGNlbGxzLnNjZS5saXN0JFJOQSkKCnNldS5ybmEgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoc2V1LnJuYSwgbmZlYXR1cmVzID0gMjAwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHNlbGVjdGlvbi5tZXRob2QgPSAibXZwIiwgZGlzcGVyc2lvbi5jdXRvZmY9YygwLjcsIDEwMCksIG1lYW4uY3V0b2ZmPWMoMC4wMiwgMykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCmh2Zy5ybmEgPC0gVmFyaWFibGVGZWF0dXJlcyhzZXUucm5hKQoKVmFyaWFibGVGZWF0dXJlUGxvdChzZXUucm5hKQpVcFNldFI6OnVwc2V0KFVwU2V0Ujo6ZnJvbUxpc3QobGlzdChIVkcuUk5BPWh2Zy5ybmEsIEhDRy5BVEFDPWhjZy5hdGFjKSkpCmBgYAoKUmVtb3ZlIGNlbGwgY3ljbGUgZ2VuZXMsIHRoYXQgbWlnaHQgaW50ZXJmZXJlIHdpdGggcHNldWRvdGltZSBvcmRlcmluZwpgYGB7cn0KY2VsbF9jeWNsZV9nZW5lcyA8LSByZWFkLnRhYmxlKCJ+L2Fubm90YXRpb25zL2NlbGxfY3ljbGVfZ2VuZXMudHN2IikkVjEKCmludGVncmF0ZV9mZWF0dXJlc191bmlvbiA8LSB1bmlvbihodmcucm5hLCBoY2cuYXRhYykKaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uIDwtIHNldGRpZmYoaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uLCBjZWxsX2N5Y2xlX2dlbmVzKSAKCiMjIFNlbGVjdCBmZWF0dXJlcyBpbiBib3RoIGRhdGFzZXRzCmludGVncmF0ZV9mZWF0dXJlc191bmlvbiA8LSBpbnRlcnNlY3QoaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uLCBpbnRlcnNlY3Qocm93bmFtZXModGNlbGxzLnNjZS5saXN0JEFUQUMpLCByb3duYW1lcyh0Y2VsbHMuc2NlLmxpc3QkUk5BKSkpIAoKYGBgCgpWaXN1YWxpemUgVCBjZWxscyBpbiBSTkEgZGF0YXNldApgYGB7cn0KdGNlbGxzLnNldS5saXN0IDwtIG1hcCh0Y2VsbHMuc2NlLmxpc3QsIH4gYXMuU2V1cmF0KC54KSkKdGNlbGxzLlJOQS51bmlvbiA8LSB0Y2VsbHMuc2V1Lmxpc3QkUk5BClZhcmlhYmxlRmVhdHVyZXModGNlbGxzLlJOQS51bmlvbikgPC0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uCnRjZWxscy5STkEudW5pb24gPC0gU2NhbGVEYXRhKHRjZWxscy5STkEudW5pb24pICU+JSBSdW5QQ0EoKSAlPiUgUnVuVU1BUChkaW1zPTE6NDApCgpEaW1QbG90KHRjZWxscy5STkEudW5pb24sIGdyb3VwLmJ5ID0gImFubm90YXRpb24iLCBsYWJlbD1UUlVFKSArIGdndGl0bGUoIlJOQSAtIGZlYXR1cmUgdW5pb24iKQpgYGAKClZpc3VhbGl6ZSBtYXJrZXJzIApgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTEwfQp0LmNlbGwubWFya2VycyA8LSBsaXN0KGtub3duLm1hcmtlcnMgPSBjKCJDRDM0IiwgIklHTEwxIiwgIlRSR0MyIiwgIlRSREMiLCAiUFRDUkEiLCAiVFJCQzIiLCAiVFJBQyIsICJDRDQiLCAiQ0Q4QSIsICJDRDhCIiksCiAgICAgICAgICAgICAgICAgICAgICAgY2hlbW9raW5lLnJlY2VwdG9ycyA9IGMoIkNDUjkiLCAiQ0NSNyIpLAogICAgICAgICAgICAgICAgICAgICAgIHRjci5hY3RpdmF0aW9uID0gYygiQ0Q1IiwgIkNEMjciKSwKICAgICAgICAgICAgICAgICAgICAgICBwcm9saWZlcmF0aW9uPWMoIlBDTkEiLCAiQ0RLMSIsICJNS0k2NyIpLAogICAgICAgICAgICAgICAgICAgICAgIGN5Y2xpbi5EID0gYygiQ0NORDIiLCAiQ0NORDMiKSwKICAgICAgICAgICAgICAgICAgICAgICByZWNvbWJpbmF0aW9uPWMoIlJBRzEiLCAiUkFHMiIpLAogICAgICAgICAgICAgICAgICAgICAgIGFwb3B0b3Npcz1jKCJIUksiLCJCTUYiLCAiVFA1M0lOUDEiKSwKICAgICAgICAgICAgICAgICAgICAgICBzdGFnZS5tYXJrZXJzID0gYygiU1QxOCIsICJISVZFUDMiLCAiUkdQRDMiLCAiU01QRDMiLCAiQVFQMyIsICJST1JDIiwgIlNBVEIxIiwgIlRPWDIiKQogICAgICAgICAgICAgICAgICAgICAgICkgCiMgRmVhdHVyZVBsb3QodGNlbGxzLlJOQS5yZWYsIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMCkpCkZlYXR1cmVQbG90KHRjZWxscy5STkEudW5pb24sIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMCkpCmBgYAoKVmlzdWFsaXplIFQgY2VsbHMgaW4gQVRBQyBkYXRhc2V0OiBpcyB0aGUgdHJhamVjdG9yeSB2aXNpYmxlIGluIHRoZSBiaW5hcnkgbWF0cml4PwpgYGB7cn0KdGNlbGxzLkFUQUMudW5pb24gPC0gdGNlbGxzLnNldS5saXN0JEFUQUMKIyB0Y2VsbHMuQVRBQy51bmlvbiA8LSBOb3JtYWxpemVEYXRhKHRjZWxscy5BVEFDLnVuaW9uKQpWYXJpYWJsZUZlYXR1cmVzKHRjZWxscy5BVEFDLnVuaW9uKSA8LSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24KdGNlbGxzLkFUQUMudW5pb24gPC0gUnVuTFNJKHRjZWxscy5BVEFDLnVuaW9uLCBuPTUwLCBzY2FsZS5tYXggPSBOVUxMKQp0Y2VsbHMuQVRBQy51bmlvbiA8LSBSdW5VTUFQKHRjZWxscy5BVEFDLnVuaW9uLCByZWR1Y3Rpb24gPSAibHNpIiwgZGltcyA9IDE6NTApCgpEaW1QbG90KHRjZWxscy5BVEFDLnVuaW9uLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsIGxhYmVsID0gVFJVRSkgKyBnZ3RpdGxlKCJBVEFDIGdtYXQiKQpgYGAKCiMjIyMgUnVuIENDQSBhbmQgQ29ub3MKClJ1biBDQ0EKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD01fQpzY2UubGlzdCA8LSB0Y2VsbHMuc2NlLmxpc3QKcmVmZXJlbmNlID0gIlJOQSIKcXVlcnkgPSAiQVRBQyIgCnNldXJhdC5saXN0IDwtIGltYXAoc2NlLmxpc3QsIH4gYXMuU2V1cmF0KC54LCBhc3NheT0ueSkpCnNldXJhdC5saXN0IDwtIGltYXAoc2V1cmF0Lmxpc3QsIH4gUmVuYW1lQ2VsbHMoLngsIGFkZC5jZWxsLmlkPS55KSkKIyMgU2NhbGUgZGF0YQpzZXVyYXQubGlzdCA8LSBtYXAoc2V1cmF0Lmxpc3QsIH4gU2NhbGVEYXRhKC54KSkKIyMgQ2FsY3VsYXRlIENDQSBhbmNob3JzCnRyYW5zZmVyLmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXVyYXQubGlzdFtbcmVmZXJlbmNlXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnkgPSBzZXVyYXQubGlzdFtbcXVlcnldXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJjY2EiKQoKIyMgSW1wdXRlIGV4cHJlc3Npb24gcHJvZmlsZXMgZm9yIEFUQUMgY2VsbHMgKGZvciBhbGwgZ2VuZXMsIG5vdCBqdXN0IGludGVncmF0aW9uIGZlYXR1cmVzKQpyZWZkYXRhIDwtIEdldEFzc2F5RGF0YShzZXVyYXQubGlzdCRSTkEsIGFzc2F5ID0gIlJOQSIsIHNsb3QgPSAiZGF0YSIpCmltcHV0YXRpb24gPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IHRyYW5zZmVyLmFuY2hvcnMsIHJlZmRhdGEgPSByZWZkYXRhLCB3ZWlnaHQucmVkdWN0aW9uID0gc2V1cmF0Lmxpc3QkQVRBQ1tbIkxTSSJdXSkKCiMjIE1lcmdlIGRhdGFzZXRzIGFuZCBjby1lbWJlZApzZXVyYXQubGlzdCRBVEFDW1siUk5BIl1dIDwtIGltcHV0YXRpb24KY29lbWJlZCA8LSBtZXJnZSh4ID0gc2V1cmF0Lmxpc3QkUk5BLCB5ID0gc2V1cmF0Lmxpc3QkQVRBQykKCmNvZW1iZWQgPC0gU2NhbGVEYXRhKGNvZW1iZWQsIGZlYXR1cmVzID0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uLCBkby5zY2FsZSA9IEZBTFNFKQpjb2VtYmVkIDwtIFJ1blBDQShjb2VtYmVkLCBmZWF0dXJlcyA9IGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgdmVyYm9zZSA9IEZBTFNFKQpjb2VtYmVkIDwtIFJ1blVNQVAoY29lbWJlZCwgZGltcyA9IDE6MzApCgpjb2VtYmVkIDwtIEFkZE1ldGFEYXRhKGNvZW1iZWQsIG1ldGFkYXRhID0gaWZlbHNlKGNvbG5hbWVzKGNvZW1iZWQpICVpbiUgY29sbmFtZXMoc2V1cmF0Lmxpc3RbW3JlZmVyZW5jZV1dKSwgcmVmZXJlbmNlLCBxdWVyeSksIGNvbC5uYW1lID0gInRlY2giKQoKRGltUGxvdChjb2VtYmVkLCBncm91cC5ieSA9IGMoJ3RlY2gnLCAiYW5ub3RhdGlvbiIpKQpgYGAKCjwhLS0gUnVuIENvbm9zIC0tPgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBkYXRhLnByb2Nlc3NlZCA8LSBtYXAoc2NlLmxpc3QsIH4gYXMuU2V1cmF0KC54KSkgIC0tPgo8IS0tIFZhcmlhYmxlRmVhdHVyZXMoZGF0YS5wcm9jZXNzZWRbW3JlZmVyZW5jZV1dKSA8LSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24gLS0+CjwhLS0gVmFyaWFibGVGZWF0dXJlcyhkYXRhLnByb2Nlc3NlZFtbcXVlcnldXSkgPC0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uIC0tPgo8IS0tIGRhdGEucHJvY2Vzc2VkIDwtIG1hcChkYXRhLnByb2Nlc3NlZCwgfiBTY2FsZURhdGEoLngpICU+JSBSdW5QQ0EoZGltcz0xOjMwKSkgLS0+CjwhLS0gbC5jb24gPC0gQ29ub3MkbmV3KGRhdGEucHJvY2Vzc2VkLG4uY29yZXM9MzApIC0tPgo8IS0tIGwuY29uJGJ1aWxkR3JhcGgoaz0xNSxrLnNlbGY9NSxrLnNlbGYud2VpZ2g9MC4wMSxuY29tcHM9MzAsbi5vZGdlbmVzPTVlMyxzcGFjZT0nUENBJykgIC0tPgoKPCEtLSBsLmNvbiRmaW5kQ29tbXVuaXRpZXMocmVzb2x1dGlvbj0xLjUpIC0tPgo8IS0tIGwuY29uJGVtYmVkR3JhcGgoYWxwaGE9MS8yKSAtLT4KCjwhLS0gY29ub3Mub3V0IDwtIGNvbm9zLm1vZGVsJG1vZGVsIC0tPgo8IS0tIGwuY29uJHBsb3RHcmFwaChjb2xvci5ieSA9ICJzYW1wbGUiKSAtLT4KCjwhLS0gZ2VuZVggPC0gc2V1cmF0Lmxpc3RbW3JlZmVyZW5jZV1dQGFzc2F5cyRSTkFAc2NhbGUuZGF0YVszLF0gLS0+CjwhLS0gZ2VuZVggPC0gc2V0TmFtZXMoYW5ub3RhdGlvblssMV0sIHJvd25hbWVzKGFubm90YXRpb24pKSAtLT4KPCEtLSBuZXcubGFiZWwucHJvYmFiaWxpdGllcyA8LSBsLmNvbiRwcm9wYWdhdGVMYWJlbHMobGFiZWxzID0gZ2VuZVgsIHZlcmJvc2UgPSBULCBmaXhlZC5pbml0aWFsLmxhYmVscz1UKSAtLT4KPCEtLSBoaXN0KG5ldy5sYWJlbC5wcm9iYWJpbGl0aWVzKSAtLT4KPCEtLSBsLmNvbiRjb3JyZWN0R2VuZXMoZ2VuZXMgPSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24sIGNvdW50Lm1hdHJpeCA9IE1hdHJpeChzZXVyYXQubGlzdCRBVEFDQGFzc2F5cyRBVEFDQGRhdGEpKSAtLT4KCjwhLS0gYGBgIC0tPgoKYGBge3IsIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD05fQpGZWF0dXJlUGxvdChjb2VtYmVkLCBmZWF0dXJlcyA9IHQuY2VsbC5tYXJrZXJzJHN0YWdlLm1hcmtlcnMsIHNwbGl0LmJ5ID0gInRlY2giLCBjb2xzID0gdmlyaWRpczo6dmlyaWRpcyhuPTEwMCkpICsgZ2d0aXRsZSgiU3RhZ2UgTWFya2VycyIpCmBgYApgYGB7ciwgZmlnLmhlaWdodD0yNSwgZmlnLndpZHRoPTl9CkZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMka25vd24ubWFya2Vycywgc3BsaXQuYnkgPSAidGVjaCIsIHNsb3QgPSAiZGF0YSIsIGNvbHMgPSB2aXJpZGlzOjp2aXJpZGlzKG49MTAwKSkKYGBgCmBgYHtyfQpGZWF0dXJlUGxvdChjb2VtYmVkLCBmZWF0dXJlcyA9IHQuY2VsbC5tYXJrZXJzJHJlY29tYmluYXRpb24sIHNwbGl0LmJ5ID0gInRlY2giLCBzbG90ID0gImRhdGEiLCBjb2xzID0gdmlyaWRpczo6dmlyaWRpcyhuPTEwMCkpIApgYGAKClRyYW5zZmVyIGxhYmVscyBvbiBBVEFDIGRhdGFzZXQKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD01fQpjZWxsdHlwZS5wcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gdHJhbnNmZXIuYW5jaG9ycywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWZkYXRhID0gc2V1cmF0Lmxpc3RbW3JlZmVyZW5jZV1dJGFubm90YXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0LnJlZHVjdGlvbiA9IHNldXJhdC5saXN0JEFUQUNbWyJMU0kiXV0pCgpjb2VtYmVkIDwtIEFkZE1ldGFEYXRhKGNvZW1iZWQsIG1ldGFkYXRhID0gY2VsbHR5cGUucHJlZGljdGlvbnMpCmNvZW1iZWRAbWV0YS5kYXRhICU8PiUKICByb3duYW1lc190b19jb2x1bW4oKSAlPiUKICBkcGx5cjo6bXV0YXRlKGFubm90YXRpb249aWZlbHNlKGlzLm5hKHByZWRpY3RlZC5pZCkgLCBhbm5vdGF0aW9uLCBOQSkpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygpCgpjb2VtYmVkQG1ldGEuZGF0YSA8LQogIGNvZW1iZWRAbWV0YS5kYXRhICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JQogIGRwbHlyOjptdXRhdGUoYW5ub3RhdGlvbj1pZmVsc2UoaXMubmEoYW5ub3RhdGlvbikgJiBwcmVkaWN0aW9uLnNjb3JlLm1heCA+IDAuNSwgcHJlZGljdGVkLmlkLCBhbm5vdGF0aW9uKSkgJT4lCiAgZHBseXI6Om11dGF0ZShhbm5vdGF0aW9uPWlmZWxzZShhbm5vdGF0aW9uPT0iU1AgKDIpIiwgTkEsIGFubm90YXRpb24pKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoKQpDb21iaW5lUGxvdHMoCiAgbGlzdChEaW1QbG90KGNvZW1iZWQsIGdyb3VwLmJ5ID0gYygicHJlZGljdGVkLmlkIiksIGNvbHMgPSBjZWxsLnR5cGUucGFsKSArIGdndGl0bGUoInByZWRpY3Rpb24iKSwKICBEaW1QbG90KGNvZW1iZWQsIGdyb3VwLmJ5ID0gYygiYW5ub3RhdGlvbiIpLCBjb2xzID0gY2VsbC50eXBlLnBhbCkgKyBnZ3RpdGxlKCJPcmlnaW5hbCArIHByZWRpY3Rpb24iKSksCiAgbGVnZW5kID0gInRvcCIKICApCmBgYApgYGB7cn0KRmVhdHVyZVBsb3QoY29lbWJlZCwgZmVhdHVyZXMgPSAicHJlZGljdGlvbi5zY29yZS5tYXgiLCBjZWxscyA9IHdoaWNoKGNvZW1iZWQkdGVjaD09IkFUQUMiKSkgKyBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKQpgYGAKCgojIyMgUnVuIFBzZXVkb3RpbWUgYW5hbHlzaXMgCklkZW50aWZ5IGNlbGwgb2Ygb3JpZ2luIGJhc2VkIG9uIElHTEwxIGFuZCBDRDM0CmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CkZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmVzID0gYygiSUdMTDEiLCAiQ0QzNCIpLCBzcGxpdC5ieSA9ICJ0ZWNoIiwgc2xvdCA9ICJkYXRhIiwgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMDApKQpgYGAKYGBge3J9CmNlbGwub28gPC0KICBjb2VtYmVkQG1ldGEuZGF0YSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikgJT4lCiAgbXV0YXRlKElHTEwxPWNvZW1iZWRAYXNzYXlzJFJOQUBjb3VudHNbIklHTEwxIixjZWxsXSkgJT4lCiAgc2VsZWN0KGNlbGwsIGFubm90YXRpb24sIElHTEwxKSAlPiUKICBhcnJhbmdlKC1JR0xMMSkgJT4lCiAgZmlsdGVyKGFubm90YXRpb249PSJETiIpICU+JQogIHRvcF9uKDEsIElHTEwxKSAlPiUKICBwdWxsKGNlbGwpCgpjb2VtYmVkQHJlZHVjdGlvbnMkdW1hcEBjZWxsLmVtYmVkZGluZ3MgJT4lCiAgYXMudGliYmxlKHJvd25hbWVzPSJjZWxsIikgJT4lCiAgbXV0YXRlKGNlbGwub28gPSBpZmVsc2UoY2VsbCAlaW4lIGNlbGwub28sIFQsIEYpKSAlPiUKICBnZ3Bsb3QoYWVzKFVNQVBfMSwgVU1BUF8yKSkgKwogIGdlb21fcG9pbnQoY29sb3I9ImdyZXk1MCIpICsKICBnZW9tX3BvaW50KGRhdGE9LiAlPiUgZmlsdGVyKGNlbGwub28pLGNvbG9yPSdyZWQnKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGE9LiAlPiUgZmlsdGVyKGNlbGwub28pLCBhZXMobGFiZWw9ImNlbGwgb2Ygb3JpZ2luIiksIGNvbG9yPSdyZWQnKSArCiAgdGhlbWVfY293cGxvdCgpIAoKY29lbWJlZCA8LSBBZGRNZXRhRGF0YShjb2VtYmVkLCBpZmVsc2UoY29sbmFtZXMoY29lbWJlZCk9PWNlbGwub28sIFRSVUUsIEZBTFNFKSwgY29sLm5hbWUgPSAiaXJvb3RfY2VsbCIpCgogIApgYGAKCgpgYGB7cn0KbWVyZ2VkLnNjZSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChsaXN0KGNvdW50cz1jb2VtYmVkQGFzc2F5cyRSTkFAY291bnRzLCBsb2djb3VudHM9Y29lbWJlZEBhc3NheXMkUk5BQGRhdGEpLCBjb2xEYXRhPWNvZW1iZWRAbWV0YS5kYXRhWywgYygiYW5ub3RhdGlvbiIsICJ0ZWNoIiwgImlyb290X2NlbGwiKV0sCiAgICAgICAgICAgICAgICAgICAgIHJlZHVjZWREaW1zID0gbWFwKGNvZW1iZWRAcmVkdWN0aW9ucywgfiAueEBjZWxsLmVtYmVkZGluZ3MpKQoKc2F2ZVJEUyhvYmplY3QgPSBtZXJnZWQuc2NlLCAifi9teV9kYXRhL1RjZWxsc19DQ0FfaW50ZWdyYXRpb25fMjAxOTEyMDMuUkRTIikKc2F2ZVJEUyhvYmplY3QgPSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24sICJ+L215X2RhdGEvaW50RmVhdHVyZXNfVGNlbGxzX0NDQV9pbnRlZ3JhdGlvbl8yMDE5MTIwMy5SRFMiKQpgYGAKCkkgaW5mZXIgcHNldWRvdGltZSB1c2luZyB0aGUgZGlmZnVzaW9uIHBzZXVkb3RpbWUgYWxnb3JpdGhtIGFzIGltcGxlbWVudGVkIGluIHNjYW5weS4gTWFraW5nIGFuIFIvcmV0aWN1bGF0ZSB3cmFwcGVyIGZvciB0aGlzIGZ1bmN0aW9uIHdvdWxkIGJlIG5pY2UsIGJ1dCBmb3Igbm93LCBzZWUgYG11bHRpT21pY19iZW5jaG1hcmsvRFBUX3RjZWxscy5pcHluYmAuCgpSZWFkIHNjYW5weSBvdXRwdXQgYW5kIHNhdmUgaW4gUiBvYmplY3QuCmBgYHtyfQpkcHQgPC0gcmVhZC5jc3YoJ34vbXlfZGF0YS9UY2VsbHNfQ0NBX2ludGVncmF0aW9uXzIwMTkxMTI3X3NjYW5weV9kcHQuY3N2JykgJT4lCiAgc2VsZWN0KFgsIGRwdF9wc2V1ZG90aW1lKQoKY29lbWJlZCA8LSBBZGRNZXRhRGF0YShjb2VtYmVkLCBjb2x1bW5fdG9fcm93bmFtZXMoZHB0LCAnWCcpKQpzYXZlUkRTKGNvZW1iZWQsICJ+L215X2RhdGEvVGNlbGxzX0NDQV9pbnRlZ3JhdGlvbl9zZXVyYXRfMjAxOTEyMDMuUm1kIikKY29lbWJlZCA8LSByZWFkUkRTKCJ+L215X2RhdGEvVGNlbGxzX0NDQV9pbnRlZ3JhdGlvbl9zZXVyYXRfMjAxOTEyMDMuUm1kIikKYGBgCgoKVmlzdWFsaXplIHBzZXVkb3RpbWUKCmBgYHtyLCBmaWcud2lkdGg9MTB9CkZlYXR1cmVQbG90KGNvZW1iZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZmVhdHVyZSA9ICJkcHRfcHNldWRvdGltZSIsIHNwbGl0LmJ5ID0gInRlY2giLCBjb2w9dmlyaWRpczo6dmlyaWRpcygxMCkpIApgYGAKClNhdmUgZmlndXJlCmBgYHtyLCBmaWcud2lkdGg9MTB9CmNvZW1iZWQudW1hcHMucGwgPC0gcGxvdF9ncmlkKAogIERpbVBsb3QoY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCJ0ZWNoIikpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpLAogIERpbVBsb3QoY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCJhbm5vdGF0aW9uIiksIGNvbHMgPSBjZWxsLnR5cGUucGFsLCBsYWJlbCA9IFRSVUUsIGxhYmVsLnNpemUgPSA1KSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksCiAgRmVhdHVyZVBsb3QoY29lbWJlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBmZWF0dXJlID0gImRwdF9wc2V1ZG90aW1lIikgKyBzY2FsZV9jb2xvcl92aXJpZGlzX2MobmFtZT0iRGlmZnVzaW9uXG5wc2V1ZG90aW1lIikgKyBnZ3RpdGxlKCIiKSwKICBucm93PTEsIG5jb2w9MywgcmVsX3dpZHRocyA9IGMoMSwxLDEuMiksCiAgbGFiZWxzID0gYygiQSIsICJCIiwgIkMiKQopIAoKY29lbWJlZC51bWFwcy5wbCArCiAgZ2dzYXZlKHBhc3RlMChvdXRkaXIsICJjb2VtYmVkX3VtYXBzLnBuZyIpLCB3aWR0aD0xMiwgaGVpZ2h0ID0gNCkKYGBgCgoKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KY29lbWJlZEBtZXRhLmRhdGEgJT4lCiAgZHBseXI6Om11dGF0ZShgRFBUIHJhbmtgPWRlbnNlX3JhbmsoZHB0X3BzZXVkb3RpbWUpKSAlPiUKICBnZ3Bsb3QoYWVzKGRwdF9wc2V1ZG90aW1lKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyhmaWxsPWFubm90YXRpb24pLCBiaW5zPTUwKSArCiAgZmFjZXRfZ3JpZChhbm5vdGF0aW9ufnRlY2gsIHNjYWxlcz0iZnJlZV95IikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY2VsbC50eXBlLnBhbCkKCmBgYAoKQ2hlY2sgZXhwcmVzc2lvbiBvZiBtYXJrZXJzIGFsb25nIHBzZXVkb3RpbWUKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD00fQpjb2VtYmVkQGFzc2F5cyRSTkFAZGF0YVt0LmNlbGwubWFya2VycyRrbm93bi5tYXJrZXJzLCBdICU+JQogIGFzLm1hdHJpeCgpICU+JQogIHJlc2hhcGUyOjptZWx0KHZhcm5hbWVzPWMoImdlbmUiLCAiY2VsbCIpKSAlPiUKICBsZWZ0X2pvaW4oY29lbWJlZEBtZXRhLmRhdGFbLCJkcHRfcHNldWRvdGltZSIsIGRyb3A9Rl0gJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpKSAlPiUKICBtdXRhdGUocHNldWRvdGltZS5yYW5rPWRlbnNlX3JhbmsoZHB0X3BzZXVkb3RpbWUpKSAlPiUKICBncm91cF9ieShnZW5lKSAlPiUKICBhcnJhbmdlKHBzZXVkb3RpbWUucmFuaykgJT4lCiAgIyBtdXRhdGUodmFsdWU9KHZhbHVlLW1pbih2YWx1ZSkpL21heCh2YWx1ZSktbWluKHZhbHVlKSkgJT4lCiAgbXV0YXRlKHZhbHVlPXpvbzo6cm9sbG1lYW4odmFsdWUsIGs9NSwgZmlsbD1OQSkpICU+JSAKICAjIG11dGF0ZSh2YWx1ZT0odmFsdWUtbWVhbih2YWx1ZSkpL3NkKHZhbHVlKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShnZW5lPWZhY3RvcihnZW5lLCBsZXZlbHM9cmV2KHVuaXF1ZShnZW5lKSkpKSAlPiUKICBnZ3Bsb3QoYWVzKHBzZXVkb3RpbWUucmFuaywgZ2VuZSwgZmlsbD12YWx1ZSkpICsKICBnZW9tX3RpbGUoKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZT0ibG9nIGV4cHJlc3Npb24iKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKQmluIHBzZXVkb3RpbWUgYW5kIHZpc3VhbGl6ZSBjZWxsIHR5cGUgY29tcG9zaXRpb24KCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9NH0KZHB0LmRmIDwtIAogIGNvZW1iZWRAbWV0YS5kYXRhICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpICU+JQogIGRwbHlyOjptdXRhdGUoZHB0X3Jhbms9ZGVuc2VfcmFuayhkcHRfcHNldWRvdGltZSkpICU+JQogIG11dGF0ZShkcHRfYmluPWN1dChkcHRfcmFuaywgYnJlYWtzID0gMTAwKSkgJT4lCiAgbXV0YXRlKGRwdF9iaW49YXMubnVtZXJpYyhkcHRfYmluKSkgJT4lCiAgc2VsZWN0KGNlbGwsdGVjaCwgYW5ub3RhdGlvbiwgcHJlZGljdGlvbi5zY29yZS5tYXgsIGRwdF9iaW4sIGRwdF9wc2V1ZG90aW1lLCBkcHRfcmFuaykKCmNlbGwudHlwZS5wbCA8LSBkcHQuZGYgJT4lCiAgZ2dwbG90KGFlcyhkcHRfYmluLCBmaWxsID0gYW5ub3RhdGlvbikpICsKICAjIGdlb21faGlzdG9ncmFtKGJpbnM9MTAwKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwsIG5hLnZhbHVlPSJncmV5NTAiKSArCiAgZmFjZXRfZ3JpZCh0ZWNofi4sIHNjYWxlcz0iZnJlZV95IikgKwogIHhsYWIoIlBzZXVkb3RpbWUgYmluIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KQoKY2VsbC50eXBlLnBsCmBgYAoKQ29ycmVsYXRpb24gYmV0d2VlbiBnbG9iYWwgYWNjZXNzaWJpbGl0eSBhbmQgcHNldWRvdGltZSBvcmRlcmluZy4KCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9OH0Kc25hcC5vdXQgPC0gcmVhZFJEUyhmaWxlID0gIn4vbXlfZGF0YS9jZWxscmFuZ2VyLWF0YWMxMTBfY291bnRfMzA0MzlfV1NTUzgwMzgzNjBfR1JDaDM4LTFfMV8wLnNuYXBBVEFDLlJEUyIpCiMgYXRhYy5kcHQuZGYgPC0gCiMgICBjb2VtYmVkQG1ldGEuZGF0YSAlPiUKIyAgIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpICU+JQojICAgZmlsdGVyKHRlY2g9PSJBVEFDIikgJT4lCiMgICAjIGdyb3VwX2J5KHRlY2gpICU+JQojICAgZHBseXI6Om11dGF0ZShkcHRfcmFuaz1kZW5zZV9yYW5rKGRwdF9wc2V1ZG90aW1lKSkgJT4lCiMgICBtdXRhdGUoZHB0X2Jpbj1jdXQoZHB0X3JhbmssIGJyZWFrcyA9IDEwMCkpICU+JQojICAgbXV0YXRlKGRwdF9iaW49YXMubnVtZXJpYyhkcHRfYmluKSkgJT4lCiMgICAjIHVuZ3JvdXAoKSAlPiUKIyAgIHNlbGVjdChjZWxsLHRlY2gsIGFubm90YXRpb24sIHByZWRpY3Rpb24uc2NvcmUubWF4LCBkcHRfYmluLCBkcHRfcHNldWRvdGltZSkKCmdyb3VwcyA8LSBkcHQuZGZbZHB0LmRmJHRlY2g9PSJBVEFDIiwgYygiY2VsbCIsICJkcHRfYmluIildCmJtYXQgPC0gc25hcC5vdXRAYm1hdFtzdHJfcmVtb3ZlKGdyb3VwcyRjZWxsLCAiQVRBQ18iKSxdCmZyYWMuYWNjZXNzaWJsZSA8LSByb3dTdW1zKGJtYXQpL25jb2woYm1hdCkKYWNjLmZyYWN0aW9uLnBsIDwtIGdyb3VwcyAlPiUKICBtdXRhdGUoZnJhY19hY2Nlc3NpYmxlPWZyYWMuYWNjZXNzaWJsZVtzdHJfcmVtb3ZlKGNlbGwsICJBVEFDXyIpXSkgJT4lCiAgZ2dwbG90KGFlcyhkcHRfYmluLCBmcmFjX2FjY2Vzc2libGUpKSArCiAgZ2VvbV9ib3hwbG90KGFlcyhncm91cD1hcy5mYWN0b3IoZHB0X2JpbikpLCBvdXRsaWVyLmFscGhhID0gMC4zLCBvdXRsaWVyLnNpemUgPSAwLjcpICsKICAjIGdlb21faml0dGVyKGFscGhhPTAuMSkgKwogIHhsYWIoIlBzZXVkb3RpbWUgYmluIikgKwogIHlsYWIoIkZyYWN0aW9uIG9mIGFjY2Vzc2libGUgYmlucyIpICsKICBmYWNldF9ncmlkKCdBVEFDJ34uKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpIAogIApkcHQucGwgPC0gcGxvdF9ncmlkKGNlbGwudHlwZS5wbCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSwgCiAgICAgICAgICBhY2MuZnJhY3Rpb24ucGwsIAogICAgICAgICAgYWxpZ24gPSAidiIsIG5jb2w9MSwgbnJvdz0yLCBheGlzPSJsIikKCmRwdC5wbCArCiAgZ2dzYXZlKHBhc3RlMChvdXRkaXIsICJEUFRfYmlucy5wbmciKSwgd2lkdGg9MTAsIGhlaWdodCA9IDcpCmBgYAoKCjwhLS0gVml6IG1hcmtlcnMgLS0+CjwhLS0gYGBge3J9IC0tPgo8IS0tIGFjYy5tYXQgPC0gY29lbWJlZEBhc3NheXMkQVRBQ0BkYXRhIC0tPgo8IS0tIG1hcmtlcnMuYWNjIDwtIGFjYy5tYXRbaW50ZXJzZWN0KGModC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgdC5jZWxsLm1hcmtlcnMkY2hlbW9raW5lLnJlY2VwdG9ycywgdC5jZWxsLm1hcmtlcnMkcmVjb21iaW5hdGlvbiksIHJvd25hbWVzKGFjYy5tYXQpKSwsIGRyb3A9Rl0gLS0+Cgo8IS0tIG1hcmtlcnMuZGYgPC0gZGF0YS5mcmFtZSh0KGFzLm1hdHJpeChtYXJrZXJzLmFjY1ssZHB0LmRmJGNlbGxbZHB0LmRmJHRlY2g9PSJBVEFDIl1dKSkpICU+JSAtLT4KPCEtLSAgIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpICU+JSAtLT4KPCEtLSAgIHBpdm90X2xvbmdlcihjb2xzID0gcm93bmFtZXMobWFya2Vycy5hY2MpLCBuYW1lc190byA9ICJtYXJrZXIuZ2VuZSIsIHZhbHVlc190byA9ICJhY2Nlc3NpYmlsaXR5IikgLS0+Cgo8IS0tIGFubm90YXRpb24uaG0gPC0gYXRhYy5kcHQuZGYgJT4lIC0tPgo8IS0tICAgZ3JvdXBfYnkoZHB0X2JpbiwgYW5ub3RhdGlvbikgJT4lIC0tPgo8IS0tICAgc3VtbWFyaXNlKG49bigpKSAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIGFubm90YXRpb24pKSArIC0tPgo8IS0tICAgZ2VvbV90aWxlKGFlcyhhbHBoYT1uLCBmaWxsPWFubm90YXRpb24pKSAgKyAtLT4KPCEtLSAgIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICsgLS0+CjwhLS0gICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y2VsbC50eXBlLnBhbCwgbmEudmFsdWU9ImdyZXk1MCIpICsgLS0+CjwhLS0gICBndWlkZXMoZmlsbD0nbm9uZScsIGFscGhhPSdub25lJykgKyAtLT4KPCEtLSAgIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkgLS0+Cgo8IS0tIG1hcmtlcnMuaG0gPC0gYXRhYy5kcHQuZGYgJT4lIC0tPgo8IS0tICAgZnVsbF9qb2luKG1hcmtlcnMuZGYpICU+JSAtLT4KPCEtLSAgIGdyb3VwX2J5KGRwdF9iaW4sIG1hcmtlci5nZW5lKSAlPiUgLS0+CjwhLS0gICBzdW1tYXJpc2UoZnJhY19hY2Nlc3NpYmxlPXN1bShhY2Nlc3NpYmlsaXR5KS9uKCkpICU+JSAtLT4KPCEtLSAgIHVuZ3JvdXAoKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUobWFya2VyLmdlbmU9ZmFjdG9yKG1hcmtlci5nZW5lLCBsZXZlbHMgPSBjKCJDRDM0IiwgIklHTEwxIiwgIlRSR0MyIiwgIlRSREMiLCAiUFRDUkEiLCAiVFJCQzIiLCAiQ0NSOSIsIkNDUjciLCAiUkFHMSIsICJSQUcyIiwgIlRSQUMiLCAiQ0Q0IiwgIkNEOEEiLCAiQ0Q4QiIpKSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKG1hcmtlci5nZW5lPWZhY3RvcihtYXJrZXIuZ2VuZSwgbGV2ZWxzID0gcmV2KGxldmVscyhtYXJrZXIuZ2VuZSkpKSkgJT4lIC0tPgo8IS0tICAgZ3JvdXBfYnkobWFya2VyLmdlbmUpICU+JSAtLT4KPCEtLSAgIG11dGF0ZShmcmFjX2FjY2Vzc2libGU9KGZyYWNfYWNjZXNzaWJsZSAtIG1pbihmcmFjX2FjY2Vzc2libGUpKS9tYXgoZnJhY19hY2Nlc3NpYmxlKSAtIG1pbihmcmFjX2FjY2Vzc2libGUpKSAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIG1hcmtlci5nZW5lLCBmaWxsPWZyYWNfYWNjZXNzaWJsZSkpICsgIC0tPgo8IS0tICAgZ2VvbV90aWxlKCkgKyAtLT4KPCEtLSAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWU9IkZyYWMuY2VsbHMiKSArIC0tPgo8IS0tICAgeGxhYigiUHNldWRvdGltZSBiaW4iKSArIC0tPgo8IS0tICAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikgKyAtLT4KPCEtLSAgIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpIC0tPgoKPCEtLSBsZWcgPC0gZ2V0X2xlZ2VuZChtYXJrZXJzLmhtKSAtLT4KPCEtLSBncjEgPC0gcGxvdF9ncmlkKGFubm90YXRpb24uaG0sIG1hcmtlcnMuaG0gKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCBucm93PTIsIHJlbF9oZWlnaHRzID0gYygxLDIpLCBhbGlnbiA9ICJ2IikgLS0+CjwhLS0gZ3IyIDwtIHBsb3RfZ3JpZChnZ3Bsb3QoKSArICB0aGVtZV92b2lkKCksbGVnLCBucm93PTIsIHJlbF9oZWlnaHRzID0gYygxLDIpKSAtLT4KPCEtLSBwbG90X2dyaWQoZ3IxLCBncjIsIHJlbF93aWR0aHMgPSBjKDMsMSkpIC0tPgo8IS0tIGBgYCAtLT4KCiMjIE1vdGlmIGFuYWx5c2lzIAoKSSBpbml0aWFsbHkgd2FudGVkIHRvIGNhbGwgcGVha3MgZnJvbSBTbmFwQVRBQyBjbHVzdGVycywgdGhlbiBidWlsZCBhIGNlbGwgeCBwZWFrIG1hdHJpeCBvbiB0aG9zZSBkZXRlY3RlZCBwZWFrcywgYnV0IFNuYXBBVEFDL01BQ1MyIHdlcmUgZHJpdmluIG1lIG51dHMuIAoKPCEtLSAtLS0tIC0tPgo8IS0tICoqVGhpcyBkb2Vzbid0IHNlZW0gdG8gd29yayoqIC0tPgo8IS0tIENhbGwgcGVha3MgIC0tPgo8IS0tIGBgYHtyfSAtLT4KPCEtLSAjIyBDYWxsIHBlYWtzIG9uIGNsdXN0ZXJzIC0tPgo8IS0tIGNsdXN0ZXJzLnNlbCA8LSB1bmlxdWUodGNlbGxzLnNjZS5hdGFjJHNldXJhdF9jbHVzdGVycykgLS0+CjwhLS0gcGVha3MubHMgPSBtY2xhcHBseShzZXEoY2x1c3RlcnMuc2VsKSwgZnVuY3Rpb24oaSl7IC0tPgo8IS0tICAgcHJpbnQocGFzdGUoImNsdXN0ZXIiLCBjbHVzdGVycy5zZWxbaV0pKSAtLT4KPCEtLSAgIHBlYWtzID0gcnVuTUFDUyggLS0+CjwhLS0gICAgICAgb2JqPXNuYXAub3V0W3doaWNoKHNuYXAub3V0QG1ldGFEYXRhJGJhcmNvZGUgJWluJSBjb2xuYW1lcyh0Y2VsbHMuc2NlLmF0YWMpW3RjZWxscy5zY2UuYXRhYyRzZXVyYXRfY2x1c3RlcnM9PWNsdXN0ZXJzLnNlbFtpXV0pLF0sICAtLT4KPCEtLSAgICAgICBvdXRwdXQucHJlZml4PXBhc3RlMCgiVGNlbGxzX0Y3NF9jbHVzdGVyIiwgY2x1c3RlcnMuc2VsW2ldKSwgLS0+CjwhLS0gICAgICAgcGF0aC50by5zbmFwdG9vbHM9Ii9vcHQvY29uZGEvYmluL3NuYXB0b29scyIsIC0tPgo8IS0tICAgICAgIHBhdGgudG8ubWFjcz0iL29wdC9jb25kYS9iaW4vbWFjczIiLCAtLT4KPCEtLSAgICAgICBnc2l6ZT0iaHMiLCAjIG1tLCBocywgZXRjIC0tPgo8IS0tICAgICAgIGJ1ZmZlci5zaXplPTUwMCwgIC0tPgo8IS0tICAgICAgIG51bS5jb3Jlcz0zLCAtLT4KPCEtLSAgICAgICBtYWNzLm9wdGlvbnM9Ii0tbm9tb2RlbCAtLXNoaWZ0IDEwMCAtLWV4dCAyMDAgLS1xdmFsIDVlLTIgLUIgLS1TUE1SIiwgLS0+CjwhLS0gICAgICAgdG1wLmZvbGRlcj10ZW1wZGlyKCkgLS0+CjwhLS0gICkgLS0+CjwhLS0gcGVha3MgLS0+CjwhLS0gfSwgbWMuY29yZXM9NSkgLS0+Cgo8IS0tIHBlYWtzLm5hbWVzID0gbGlzdC5maWxlcygifi9teV9kYXRhL1RjZWxsc19wZWFrcy8iLCBwYXR0ZXJuPSJuYXJyb3dQZWFrIiwgZnVsbC5uYW1lcyA9IFQpIC0tPgo8IS0tIHBlYWsuZ3IubHMgPSBsYXBwbHkocGVha3MubmFtZXMsIGZ1bmN0aW9uKHgpeyAtLT4KPCEtLSAgIHBlYWsuZGYgPSByZWFkLnRhYmxlKHgpIC0tPgo8IS0tICAgR1JhbmdlcyhzdHJfcmVtb3ZlX2FsbChwZWFrLmRmWywxXSwgImInfCciKSwgSVJhbmdlcyhwZWFrLmRmWywyXSwgcGVhay5kZlssM10pKSAtLT4KPCEtLSB9KSAtLT4KPCEtLSBwZWFrLmdyID0gcmVkdWNlKFJlZHVjZShjLCBwZWFrLmdyLmxzKSkgLS0+Cgo8IS0tICMjIE1ha2UgY2VsbCBieSBwZWFrIG1hdHJpeCAobm90IHJ1biBoZXJlKSAtLT4KPCEtLSBwZWFrcy5kZiA9IGFzLmRhdGEuZnJhbWUocGVhay5ncilbLDE6M107IC0tPgo8IS0tIHdyaXRlLnRhYmxlKHBlYWtzLmRmLGZpbGUgPSAifi9teV9kYXRhL1RjZWxsc19wZWFrcy9wZWFrcy5jb21iaW5lZC5iZWQiLGFwcGVuZD1GQUxTRSwgLS0+CjwhLS0gCQlxdW90ZT0gRkFMU0Usc2VwPSJcdCIsIGVvbCA9ICJcbiIsIG5hID0gIk5BIiwgZGVjID0gIi4iLCAgLS0+CjwhLS0gCQlyb3cubmFtZXMgPSBGQUxTRSwgY29sLm5hbWVzID0gRkFMU0UsIHFtZXRob2QgPSBjKCJlc2NhcGUiLCAiZG91YmxlIiksIC0tPgo8IS0tIAkJZmlsZUVuY29kaW5nID0gIiIpIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gTWFraW5nIGNvbW1vbiBwZWFrIHJlZmVyZW5jZSB3aXRoIHNuYXB0b29scy4gSW4gdGVybWluYWwgLS0+CjwhLS0gYGBgIC0tPgo8IS0tIHNuYXB0b29scyBzbmFwLWFkZC1wbWF0IC0tc25hcC1maWxlIH4vbXlfZGF0YS9jZWxscmFuZ2VyLWF0YWMxMTBfY291bnRfMzA0MzlfV1NTUzgwMzgzNjBfR1JDaDM4LTFfMV8wLnNuYXAgLS1wZWFrLWZpbGUgcGVha3MuY29tYmluZWQuYmVkICAtLT4KPCEtLSBgYGAgLS0+Cgo8IS0tIEFkZCBwbWF0IHRvIHNuYXAgb2JqZWN0IC0tPgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBzbmFwLm91dCA8LSBjcmVhdGVQbWF0KHNuYXAub3V0LCBwZWFrLmdyLCBkby5wYXIgPSBULCBudW0uY29yZXMgPSAxMCkgLS0+CjwhLS0gYGBgIC0tPgo8IS0tIC0tLSAtLT4KCkFsdGVybmF0aXZlOiBsb2FkIHBlYWsgbWF0cml4IGZyb20gY2VsbHJhbmdlciBhbmQgYWRkIHRvIHNuYXAgb2JqZWN0CmBgYHtyfQpmaWx0LnBlYWtzIDwtIFJlYWQxMFhfaDUoIn4vbXlfZGF0YS9maWx0ZXJlZF9wZWFrX2JjX21hdHJpeC5oNSIpCnBlYWtzLm1hdCA8LSBzdHJfc3BsaXQocm93bmFtZXMoZmlsdC5wZWFrcyksIHBhdHRlcm4gPSAiOnwtIikgJT4lIG1hcChyYmluZCkgJT4lIHB1cnJyOjpyZWR1Y2UocmJpbmQpCnBlYWtzLmdyIDwtIEdSYW5nZXMocGVha3MubWF0WywxXSwgSVJhbmdlcyhhcy5udW1lcmljKHBlYWtzLm1hdFssMl0pLCBhcy5udW1lcmljKHBlYWtzLm1hdFssM10pKSkKc25hcC5wbWF0IDwtIGNyZWF0ZVNuYXBGcm9tUG1hdChtYXQ9dChmaWx0LnBlYWtzWyxzbmFwLm91dEBiYXJjb2RlXSksIGJhcmNvZGVzPXNuYXAub3V0QGJhcmNvZGUsIHBlYWtzPXBlYWtzLmdyKQpzbmFwLnBtYXQKYGBgCgpDYWxjdWxhdGluZyBkZXZpYXRpb25zIGluIFRGIGFjY2Vzc2liaWxpdHkgdXNpbmcgQ2hyb21WQVIuIFRoaXMgaXMgYSBtZWFzdXJlIG9mIGhvdyBtdWNoIGlzIG1vdGlmIGFjY2Vzc2liaWxpdHkgaW4gZWFjaCBjZWxsIGlzIGVucmljaGVkIGNvbXBhcmVkIHRvIGFsbCB0aGUgY2VsbHMgYW5kIGdlbmVyYWwgY2VsbCBjb3ZlcmFnZS4gV2hpbGUgU25hcEFUQUMgaGFzIGFuIHdyYXBwZXIgYXJvdW5kIENocm9tVkFSIHRoYXQgb3V0cHV0cyB0aGUgZGV2aWF0aW9uIG1hdHJpeCwgSSBqdXN0IHRha2UgdGhlIGNvZGUgZnJvbSB0aGF0IGZ1bmN0aW9uIGFuZCBydW4gZXZlcnkgc3RlcCBzZXBhcmF0ZWx5IHRvIGtlZXAgdGhlIHVzZWZ1bCBvdXRwdXRzIGFuZCBzdGF0aXN0aWNzIG9mIGNocm9tVkFSLgoKYGBge3J9CnNuYXAucG1hdCA9IG1ha2VCaW5hcnkoc25hcC5wbWF0LCAicG1hdCIpCgpvYmogPSBzbmFwLnBtYXQKaW5wdXQubWF0PSJwbWF0IgptaW4uY291bnQ9MTAKc3BlY2llcz0iSG9tbyBzYXBpZW5zIgpnZW5vbWU9QlNnZW5vbWUuSHNhcGllbnMuVUNTQy5oZzM4CgpkYXRhLnVzZSA9IG9iakBwbWF0CnBlYWsudXNlID0gb2JqQHBlYWsKCm5jZWxsID0gbnJvdyhkYXRhLnVzZSkKCmlkeSA9IHdoaWNoKE1hdHJpeDo6Y29sU3VtcyhkYXRhLnVzZSkgPj0gbWluLmNvdW50KQpkYXRhLnVzZSA9IGRhdGEudXNlWyxpZHksZHJvcHBpbmc9VFJVRV0KCQpwZWFrLnVzZSA9IHBlYWsudXNlW2lkeV0KCnJzZSA8LSBTdW1tYXJpemVkRXhwZXJpbWVudCgKCQlhc3NheXMgPSBsaXN0KGNvdW50cyA9IHQoZGF0YS51c2UpKSwgCgkJCQkgcm93UmFuZ2VzID0gcGVhay51c2UsIAoJCQkJIGNvbERhdGEgPSBEYXRhRnJhbWUoQ2VsbF9UeXBlPTE6bnJvdyhkYXRhLnVzZSksIGRlcHRoPU1hdHJpeDo6cm93U3VtcyhkYXRhLnVzZSkpCgkpOwpyc2UgPC0gYWRkR0NCaWFzKHJzZSwgZ2Vub21lID0gZ2Vub21lKTsKbW90aWZzIDwtIGdldEphc3Bhck1vdGlmcyhjb2xsZWN0aW9uID0gIkNPUkUiLCBzcGVjaWVzPXNwZWNpZXMpOwptb3RpZl9tbSA8LSBtYXRjaE1vdGlmcyhtb3RpZnMsIHJzZSwgZ2Vub21lID0gZ2Vub21lKTsKZGV2IDwtIGNvbXB1dGVEZXZpYXRpb25zKG9iamVjdCA9IHJzZSwgYW5ub3RhdGlvbnMgPSBtb3RpZl9tbSk7CnZhciA8LSBjb21wdXRlVmFyaWFiaWxpdHkoZGV2KQpgYGAKClNhdmUKYGBge3J9CnJvd0RhdGEoZGV2KSAlPD4lCiAgYXMudGliYmxlKHJvd25hbWVzPSJtb3RpZiIpICU+JQogIGZ1bGxfam9pbih2YXIpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygnbW90aWYnKSAlPiUKICBEYXRhRnJhbWUoKQoKc2F2ZVJEUyhkZXYsICJ+L215X2RhdGEvVGNlbGxzX3BlYWtzL1RjZWxsc19jaHJvbVZhck91dHB1dC5SRFMiKSAgCmBgYAoKVmlzdWFsaXplIGRldmlhdGlvbiBzY29yZXMgb2YgdGhlIG1vc3QgdmFyaWFibGUgbW90aWZzLCBvcmRlcmVkIGluIHBzZXVkb3RpbWUuCgpgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTEwfQpzYW1wbGVfZHB0X2JpbnMuZGYgPC0gZHB0LmRmICU+JQogIG11dGF0ZShjZWxsPXN0cl9yZW1vdmUoY2VsbCwgIl5BVEFDXyIpKSAlPiUKICBmaWx0ZXIodGVjaD09IkFUQUMiKSAlPiUKICBhcnJhbmdlKGRwdF9wc2V1ZG90aW1lKQoKbW90aWYudG9wdmFyIDwtIHZhciAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJtb3RpZiIpICU+JSB0b3Bfbig1MCx2YXJpYWJpbGl0eSkgJT4lIHB1bGwobW90aWYpCnRmLnRvcHZhciA8LSBtb3RpZi50b3B2YXIgJT4lIHN0cl9yZW1vdmUoIi4rXyIpICU+JSBzdHJfcmVtb3ZlKCJcXCguK3w6LisiKQptbWF0LnRvcHZhciA8LSBkZXZAYXNzYXlzJGRhdGEkelttb3RpZi50b3B2YXIsc2FtcGxlX2RwdF9iaW5zLmRmJGNlbGxdCgpyb3duYW1lcyhtbWF0LnRvcHZhcikgPC0gdGYudG9wdmFyCnNtb290aC5tbWF0IDwtIGFwcGx5KG1tYXQudG9wdmFyLCAxLCBmdW5jdGlvbih4KSB6b286OnJvbGxtZWFuKHgsIGs9MzApKSAlPiUgdCgpIAoKcG5nKHBhc3RlMChvdXRkaXIsICJjaHJvbVZBUl9tb3RpZl9oZWF0bWFwLnBuZyIpLCB3aWR0aD0xMDAwLCBoZWlnaHQgPSA5MDApCnNtb290aC5tbWF0ICU+JQogICMgbW1hdC50b3B2YXJbLHNhbXBsZV9kcHRfYmlucy5kZiRjZWxsXSAlPiUKICBwaGVhdG1hcDo6cGhlYXRtYXAoc2hvd19jb2xuYW1lcyA9IEYsIGNsdXN0ZXJfY29scyA9IEYsIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gc2FtcGxlX2RwdF9iaW5zLmRmWyxjKCJjZWxsIiwgImFubm90YXRpb24iLCAiZHB0X3BzZXVkb3RpbWUiKV0gJT4lIGNvbHVtbl90b19yb3duYW1lcygiY2VsbCIpLCAKICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBsaXN0KGFubm90YXRpb249Y2VsbC50eXBlLnBhbCwgZHB0X3BzZXVkb3RpbWU9dmlyaWRpczo6dmlyaWRpcygxMDApKSwgZm9udHNpemUgPSAxOCwgZm9udHNpemVfcm93ID0gMTIsCiAgICAgICAgICAgICAgICAgICAgICMgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKHJldihicmV3ZXIucGFsKG4gPSA3LCBuYW1lID0iU3BlY3RyYWwiKSkpKDEwMCkpCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcz1zZXEoLTMsMywgbGVuZ3RoLm91dCA9IDEwMCkKICApCmRldi5vZmYoKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTB9CmRwdC5vcmRlciA8LQogIGRwdC5kZiAlPiUKICBmaWx0ZXIodGVjaD09IlJOQSIpICU+JQogIGFycmFuZ2UoZHB0X3BzZXVkb3RpbWUpIAoKY29lbWJlZCA8LSBTY2FsZURhdGEoY29lbWJlZCwgZG8uc2NhbGU9VFJVRSkKZ2V4bWF0LnRvcHZhciA8LSBjb2VtYmVkQGFzc2F5cyRSTkFAc2NhbGUuZGF0YVt0Zi50b3B2YXJbd2hpY2godGYudG9wdmFyICVpbiUgcm93bmFtZXMoY29lbWJlZEBhc3NheXMkUk5BQHNjYWxlLmRhdGEpKV0sZHB0Lm9yZGVyJGNlbGxdCnNtb290aC5nZXhtYXQgPC0gYXBwbHkoZ2V4bWF0LnRvcHZhciwgMSwgZnVuY3Rpb24oeCkgem9vOjpyb2xsbWVhbih4LCBrPTMwKSkgJT4lIHQoKSAKc21vb3RoLmdleG1hdCAlPiUKICAjIHQoKSAlPiUgc2NhbGUoKSAlPiUgdCgpICU+JQogIHBoZWF0bWFwOjpwaGVhdG1hcChzaG93X2NvbG5hbWVzID0gRiwgY2x1c3Rlcl9yb3dzID0gVCwgY2x1c3Rlcl9jb2xzID0gRiwgCiAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gZHB0Lm9yZGVyWyxjKCJjZWxsIiwgImFubm90YXRpb24iLCAiZHB0X3BzZXVkb3RpbWUiKV0gJT4lIGNvbHVtbl90b19yb3duYW1lcygiY2VsbCIpLAogICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoYW5ub3RhdGlvbj1jZWxsLnR5cGUucGFsLCBkcHRfcHNldWRvdGltZT12aXJpZGlzOjp2aXJpZGlzKDEwMCkpLCBmb250c2l6ZSA9IDE4LCBmb250c2l6ZV9yb3cgPSAxMiwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzPXNlcSgtMiwyLCBsZW5ndGgub3V0ID0gMTAwKQogICkKYGBgCgpDb21wYXJlIG1vdGlmIGFjY2Vzc2liaWxpdHkgdHJlbmQgd2l0aCBnZW5lIGV4cHJlc3Npb24gdHJlbmQgYWxvbmcgcHNldWRvdGltZS4KCmBgYHtyfQpjb3VudHMudG9wdmFyIDwtIGNvZW1iZWRAYXNzYXlzJFJOQUBkYXRhW3RmLnRvcHZhclt3aGljaCh0Zi50b3B2YXIgJWluJSByb3duYW1lcyhjb2VtYmVkQGFzc2F5cyRSTkFAZGF0YSkpXSxkcHQub3JkZXIkY2VsbF0KZ2V4LmRmIDwtIAogIHJlc2hhcGUyOjptZWx0KGFzLm1hdHJpeChjb3VudHMudG9wdmFyKSwgdmFybmFtZXM9YygiZ2VuZSIsICJjZWxsIikpICU+JSAKICAjIHJvd2lkX3RvX2NvbHVtbigiZHB0X29yZGVyIikgJT4lCiAgbXV0YXRlKGRhdGE9IkdlbmUgZXhwcmVzc2lvbiIpCm1tYXQuZGYgPC0gcmVzaGFwZTI6Om1lbHQobW1hdC50b3B2YXIsIHZhcm5hbWVzPWMoImdlbmUiLCAiY2VsbCIpKSAlPiUKICAjIHJvd2lkX3RvX2NvbHVtbigiZHB0X29yZGVyIikgJT4lCiAgbXV0YXRlKGNlbGw9c3RyX2MoIkFUQUNfIiwgY2VsbCkpICU+JQogIG11dGF0ZShkYXRhPSJNb3RpZiBkZXZpYXRpb24iKQoKcGxvdC50ZnMgPC0gYygiUlVOWDMiLCAiUlVOWDIiKQpwbG90LnRmcyA8LSBmdW5jdGlvbihwbG90LnRmcyl7CiAgYmluZF9yb3dzKGdleC5kZiwgbW1hdC5kZikgJT4lCiAgbGVmdF9qb2luKGRwdC5kZlssIGMoImNlbGwiLCAiZHB0X3BzZXVkb3RpbWUiLCAiYW5ub3RhdGlvbiIpXSwgYnk9ImNlbGwiKSAlPiUKICBtdXRhdGUoZHB0X3Jhbms9ZGVuc2VfcmFuayhkcHRfcHNldWRvdGltZSkpICU+JQogIGRyb3BfbmEoZHB0X3BzZXVkb3RpbWUpICU+JQogICAgbXV0YXRlKGRhdGE9ZmFjdG9yKGRhdGEsIGxldmVscz1jKCJHZW5lIGV4cHJlc3Npb24iLCAic21vb3RoIiwgIk1vdGlmIGRldmlhdGlvbiIpKSkgJT4lCiAgZmlsdGVyKGdlbmUgJWluJSBwbG90LnRmcykgJT4lCiAgZ2dwbG90KGFlcyhkcHRfcmFuaywgdmFsdWUsIGNvbG9yPWRhdGEpKSArCiAgIyBnZW9tX3BvaW50KGRhdGE9LiAlPiUgZmlsdGVyKGRhdGEhPSJzbW9vdGgiKSwgYWVzKGNvbG9yPWFubm90YXRpb24pLCBzaXplPTAuNywgYWxwaGE9MC4zKSArCiAgZ2VvbV9zbW9vdGgoIG1ldGhvZD0ibG9lc3MiLHNwYW49MC4yKSArCiAgZmFjZXRfZ3JpZChkYXRhfmdlbmUsIHNjYWxlcz0iZnJlZSIpICsKICB4bGFiKCJQc2V1ZG90aW1lIHJhbmsiKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpIAp9CnRmcyA8LSBjKCJKVU4iLCAiRk9TTDIiLCAiRk9TTDEiLCAiRk9TIikKCnBkZihwYXN0ZTAob3V0ZGlyLCAiVEZfcGxvdHMucGRmIiksIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSkKZm9yICh0ZiBpbiB0Zi50b3B2YXIpIHsKICBwcmludChwbG90LnRmcyh0ZikpCn0KZGV2Lm9mZigpCgptYXAobGlzdCgiSlVOIiwgIkVMSzMiLCAiUlVOWDIiLCAiUkVMIiwgIkZPUyIsICJFVFY2IiksIH4gcGxvdC50ZnMoLngpICsgZ2dzYXZlKHBhc3RlMChvdXRkaXIsIHBhc3RlMCgnVEZfcGxvdF8nLC54LCIucG5nIikpLCB3aWR0aCA9IDgsIGhlaWdodD01KSkKYGBgCgoKCjwhLS0gYGBge3IsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD03fSAtLT4KPCEtLSBiaW5kX3Jvd3MoYWNjZXNzLmRmLCBnZW5leC5kZikgJT4lIC0tPgo8IS0tICAgZmlsdGVyKGdlbmUgJWluJSBjKCJTUEkxIiwgIlJVTlgyIiwiUlVOWDMiLCAnVENGN0wyJywgIkUyRjQiKSkgJT4lIC0tPgo8IS0tICAgICBmaWx0ZXIoIXN0cl9kZXRlY3QoY2VsbCwgIl5BVEFDXyIpIHwgdGVjaD09IkFUQUMiKSAlPiUgLS0+CjwhLS0gICAjIGdyb3VwX2J5KHRlY2gsIGdlbmUpICU+JSAtLT4KPCEtLSAgICMgbXV0YXRlKHZhbHVlPXNjYWxlKHZhbHVlKSkgJT4lIC0tPgo8IS0tICAgIyB1bmdyb3VwKCkgJT4lIC0tPgo8IS0tICAgIyBkcm9wX25hKCkgJT4lIC0tPgo8IS0tICMgZmlsdGVyKHRlY2g9PSJSTkEiKSAlPiUgLS0+CjwhLS0gICBkcm9wX25hKGRwdF9iaW4pICU+JSAtLT4KPCEtLSAgIGdncGxvdChhZXMoZHB0X2JpbiwgdmFsdWUpKSArIC0tPgo8IS0tICAgZ2VvbV9wb2ludChhZXMoY29sb3I9YW5ub3RhdGlvbiksIGFscGhhPTAuMikgKyAtLT4KPCEtLSAgIGZhY2V0X2dyaWQodGVjaH5nZW5lLCBzY2FsZXMgPSAiZnJlZV95IikgKyAtLT4KPCEtLSAgIGdlb21fc21vb3RoKCkgLS0+CgoKPCEtLSBgYGAgLS0+Cgo8IS0tIGBgYHtyLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9N30gLS0+CjwhLS0gYmluZF9yb3dzKGFjY2Vzcy5kZiwgZ2VuZXguZGYpICU+JSAtLT4KPCEtLSAgIGZpbHRlcihnZW5lICVpbiUgYygiRUxLMyIsICJKVU5CIiwgIkZPUyIpKSAlPiUgLS0+CjwhLS0gICBmaWx0ZXIoIXN0cl9kZXRlY3QoY2VsbCwgIl5BVEFDXyIpIHwgdGVjaD09IkFUQUMiKSAlPiUgLS0+CjwhLS0gICAjIGdyb3VwX2J5KHRlY2gsIGdlbmUpICU+JSAtLT4KPCEtLSAgICMgbXV0YXRlKHZhbHVlPXNjYWxlKHZhbHVlKSkgJT4lIC0tPgo8IS0tICAgIyB1bmdyb3VwKCkgJT4lIC0tPgo8IS0tICAgZHJvcF9uYShkcHRfYmluKSAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIHZhbHVlKSkgKyAtLT4KPCEtLSAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPWFubm90YXRpb24pLCBhbHBoYT0wLjIpICsgLS0+CjwhLS0gICBmYWNldF9ncmlkKHRlY2h+Z2VuZSwgc2NhbGVzID0gImZyZWVfeSIpICsgLS0+CjwhLS0gICBnZW9tX3Ntb290aCgpICsgLS0+CjwhLS0gICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwpIC0tPgoKCjwhLS0gYGBgIC0tPgoKPCEtLSAjIyBQc2V1ZG90aW1lIGxhZyBiZXR3ZWVuIERQKFEpIGluIGFjY2Vzc2liaWxpdHkgYW5kIGdlbmUgZXhwcmVzc2lvbiAtLT4KPCEtLSBUaGUgRFAgKFEpIGNsdXN0ZXIgaW4gdGhlIEFUQUMgY2VsbHMgaXMgc2NvcmVkIHdpdGggaGlnaCBjb25maWRlbmNlIC0tPgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBkcHEuY29lbWJlZCA8LSBjb2VtYmVkWyx3aGljaChjb2VtYmVkJGFubm90YXRpb249PSJEUCAoUSkiKV0gLS0+Cgo8IS0tIEZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmU9InByZWRpY3Rpb24uc2NvcmUubWF4IikgLS0+CjwhLS0gRGltUGxvdChjb2VtYmVkLCBncm91cC5ieSA9ImFubm90YXRpb24iLCBzcGxpdC5ieSA9ICJ0ZWNoIikgLS0+Cgo8IS0tIGBgYCAtLT4KCjwhLS0gYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fSAtLT4KPCEtLSBjY2Eub2JqIDwtIHRyYW5zZmVyLmFuY2hvcnNAb2JqZWN0Lmxpc3RbWzFdXSAtLT4KPCEtLSBuZXcubWV0YWRhdGEgPC0gY29lbWJlZEBtZXRhLmRhdGFbLGMoImFubm90YXRpb24iLCAidGVjaCIpLCBkcm9wPUZdICU+JSByb3duYW1lc190b19jb2x1bW4oKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUocm93bmFtZT1pZmVsc2Uoc3RyX2RldGVjdChyb3duYW1lLCAiXlJOQSIpLCBzdHJfYyhyb3duYW1lLCAiX3JlZmVyZW5jZSIpLCBzdHJfYyhyb3duYW1lLCAiX3F1ZXJ5IikpKSAlPiUgLS0+CjwhLS0gICBjb2x1bW5fdG9fcm93bmFtZXMoKSAtLT4KPCEtLSBjY2Eub2JqIDwtIEFkZE1ldGFEYXRhKGNjYS5vYmosIG5ldy5tZXRhZGF0YSkgLS0+CjwhLS0gY2NhLm9iakBtZXRhLmRhdGEgLS0+CjwhLS0gRGltUGxvdChjY2Eub2JqLCBncm91cC5ieT1jKCJhbm5vdGF0aW9uIiwidGVjaCIpLCByZWR1Y3Rpb24gPSAiY2NhIiwgZGltcyA9IDE6MikgLS0+CjwhLS0gRGltUGxvdChjY2Eub2JqLCBncm91cC5ieT1jKCJhbm5vdGF0aW9uIiwidGVjaCIpLCByZWR1Y3Rpb24gPSAiY2NhIiwgZGltcyA9IDM6NCkgLS0+CjwhLS0gRGltUGxvdChjY2Eub2JqLCBncm91cC5ieT1jKCJhbm5vdGF0aW9uIiwidGVjaCIpLCByZWR1Y3Rpb24gPSAiY2NhIiwgZGltcyA9IDU6NikgLS0+CjwhLS0gYGBgIC0tPgoKPCEtLSBgYGB7ciwgZmlnLmhlaWdodD0xOCwgZmlnLndpZHRoPTE4fSAtLT4KPCEtLSB0b3AuY2MuZ2VuZXMgPC0gY2NhLm9iakByZWR1Y3Rpb25zJGNjYS5sMkBmZWF0dXJlLmxvYWRpbmdzICU+JSAgLS0+CjwhLS0gICByZXNoYXBlMjo6bWVsdCh2YXJuYW1lcz1jKCJnZW5lIiwgIkNDIikpICU+JSAtLT4KPCEtLSAgIGdyb3VwX2J5KENDKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUocmFuaz1yYW5rKGFicyh2YWx1ZSkpKSAlPiUgLS0+CjwhLS0gICB1bmdyb3VwKCkgJT4lIC0tPgo8IS0tICAgZmlsdGVyKHJhbmsgPiAobWF4KHJhbmspLTEwKSkgJT4lIC0tPgo8IS0tICAgcHVsbChnZW5lKSAlPiUgLS0+CjwhLS0gICB1bmlxdWUoKSAtLT4KCjwhLS0gYXRhYy5tYXQgPC0gY29lbWJlZEBhc3NheXMkQVRBQ0BkYXRhIC0tPgo8IS0tIHJuYS5tYXQgPC0gY29lbWJlZEBhc3NheXMkUk5BQGRhdGEgLS0+Cgo8IS0tIGF0YWMubWF0W2dlbmUub2ksXSAlPiUgLS0+CjwhLS0gICB7Llssd2hpY2goYXBwbHkoLiwyLCBmdW5jdGlvbih4KSBzdW0oeCkhPTApKV19ICU+JSAtLT4KPCEtLSAgIHBoZWF0bWFwOjpwaGVhdG1hcChzaG93X2NvbG5hbWVzPUYsIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGNvZW1iZWRAbWV0YS5kYXRhWywiYW5ub3RhdGlvbiIsIGRyb3A9Rl0gLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgKSAtLT4KPCEtLSBgYGAgLS0+CjwhLS0gYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0gLS0+CjwhLS0gZHBxLmNlbGxzIDwtIHJvd25hbWVzKG5ldy5tZXRhZGF0YVtuZXcubWV0YWRhdGEkYW5ub3RhdGlvbj09IkRQIChRKSIsXSkgLS0+CjwhLS0gZHBxLnF1ZXJ5Lml4IDwtIHdoaWNoKHRyYW5zZmVyLmFuY2hvcnNAcXVlcnkuY2VsbHMgJWluJSBkcHEuY2VsbHMpIC0tPgo8IS0tIGRwcS5yZWYuaXggPC0gd2hpY2godHJhbnNmZXIuYW5jaG9yc0ByZWZlcmVuY2UuY2VsbHMgJWluJSBkcHEuY2VsbHMpIC0tPgo8IS0tIG5ldy5tZXRhZGF0YSAlPiUgLS0+CjwhLS0gICByb3duYW1lc190b19jb2x1bW4oKSAlPiUgLS0+CjwhLS0gICBmaWx0ZXIodGVjaD09IkFUQUMiKSAtLT4KPCEtLSB0cmFuc2Zlci5hbmNob3JzQGFuY2hvcnMgJT4lIC0tPgo8IS0tICAgYXMudGliYmxlKCkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGNlbGwxPXRyYW5zZmVyLmFuY2hvcnNAcmVmZXJlbmNlLmNlbGxzW2NlbGwxXSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGNlbGwyPXRyYW5zZmVyLmFuY2hvcnNAcXVlcnkuY2VsbHNbY2VsbDJdKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUoYW5uby5jZWxsMSA9IG5ldy5tZXRhZGF0YVtjZWxsMSwgJ2Fubm90YXRpb24nXSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGFubm8uY2VsbDIgPSBuZXcubWV0YWRhdGFbY2VsbDIsICdhbm5vdGF0aW9uJ10pICU+JSAtLT4KPCEtLSAgICMgc3ByZWFkKGNlbGwyLCBzY29yZSkgIC0tPgo8IS0tICAgZ2dwbG90KGFlcyhzY29yZSkpICsgLS0+CjwhLS0gICBnZW9tX2hpc3RvZ3JhbSgpICsgLS0+CjwhLS0gICB4bGltKDAsMSkgKyAtLT4KPCEtLSAgICMgZ2VvbV90aWxlKCkgKyAtLT4KPCEtLSAgIGZhY2V0X2dyaWQoYW5uby5jZWxsMX5hbm5vLmNlbGwyLCBzY2FsZXM9ImZyZWVfeSIsIHNwYWNlPSJmcmVlIiwgbGFiZWxsZXIgPSAibGFiZWxfYm90aCIpICAtLT4KPCEtLSBgYGAgLS0+Cgo8IS0tIDwhLS0gc2hvdyBhbmNob3IgbWF0IC0tPiAtLT4KCjwhLS0gPCEtLSBgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfSAtLT4gLS0+CjwhLS0gPCEtLSBhbmNob3IubWF0IDwtIHRyYW5zZmVyLmFuY2hvcnNAYW5jaG9ycyAlPiUgLS0+IC0tPgo8IS0tIDwhLS0gICBhcy50aWJibGUoKSAlPiUgLS0+IC0tPgo8IS0tIDwhLS0gICBtdXRhdGUoY2VsbDE9dHJhbnNmZXIuYW5jaG9yc0ByZWZlcmVuY2UuY2VsbHNbY2VsbDFdKSAlPiUgLS0+IC0tPgo8IS0tIDwhLS0gICBtdXRhdGUoY2VsbDI9dHJhbnNmZXIuYW5jaG9yc0BxdWVyeS5jZWxsc1tjZWxsMl0pICU+JSAtLT4gLS0+CjwhLS0gPCEtLSAgIHNwcmVhZChjZWxsMiwgc2NvcmUpICU+JSAtLT4gLS0+CjwhLS0gPCEtLSAgIGNvbHVtbl90b19yb3duYW1lcygnY2VsbDEnKSAlPiUgLS0+IC0tPgo8IS0tIDwhLS0gICBhcy5tYXRyaXgoKSAtLT4gLS0+Cgo8IS0tIDwhLS0gYW5jaG9yLm1hdCAlPiUgIC0tPiAtLT4KPCEtLSA8IS0tICAgaWZlbHNlKGlzLm5hKC4pLCAwLCAuKSAlPiUgLS0+IC0tPgo8IS0tIDwhLS0gICBwaGVhdG1hcDo6cGhlYXRtYXAoc2hvd19yb3duYW1lcyA9IEYsIHNob3dfY29sbmFtZXMgPSBGLCAtLT4gLS0+CjwhLS0gPCEtLSAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IG5ldy5tZXRhZGF0YVssJ2Fubm90YXRpb24nLCBkcm9wPUZdLCAtLT4gLS0+CjwhLS0gPCEtLSAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IG5ldy5tZXRhZGF0YVssJ2Fubm90YXRpb24nLCBkcm9wPUZdLCAtLT4gLS0+CjwhLS0gPCEtLSAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoYW5ub3RhdGlvbj1jZWxsLnR5cGUucGFsKSkgLS0+IC0tPgo8IS0tIDwhLS0gYGBgIC0tPiAtLT4KCjwhLS0gYGBge3J9IC0tPgo8IS0tIHByZWQuc2NvcmVzIDwtIGNvbG5hbWVzKGNvZW1iZWRAbWV0YS5kYXRhKSAlPiUgc3RyX3N1YnNldCgicHJlZGljdGlvbi5zY29yZSIpICAtLT4KPCEtLSBjb2VtYmVkQG1ldGEuZGF0YSAlPiUgLS0+CjwhLS0gICBmaWx0ZXIodGVjaD09IkFUQUMiKSAlPiUgLS0+CjwhLS0gICBzZWxlY3QoYygiYW5ub3RhdGlvbiIsIHByZWQuc2NvcmVzKSkgJT4lIC0tPgo8IS0tICAgcGl2b3RfbG9uZ2VyKGNvbHM9LWFubm90YXRpb24sIG5hbWVzX3RvID0gImNsYXNzIikgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGNsYXNzPXN0cl9yZW1vdmUoY2xhc3MsICJwcmVkaWN0aW9uLnNjb3JlLiIpKSAlPiUgLS0+CjwhLS0gICBmaWx0ZXIoY2xhc3MhPSJtYXgiKSAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKGFubm90YXRpb24sIHZhbHVlLCBmaWxsPWNsYXNzKSkgKyAtLT4KPCEtLSAgIGdlb21fYm94cGxvdCgpIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gYGBge3IsIGZpZy53aWR0aD0xMH0gLS0+CjwhLS0gRGVmYXVsdEFzc2F5KGRwcS5jb2VtYmVkKSA8LSAiUk5BIiAtLT4KPCEtLSBkcHEuY29lbWJlZCA8LSBTY2FsZURhdGEoZHBxLmNvZW1iZWQsIGZlYXR1cmVzID0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uKSAtLT4KPCEtLSBkcHEuY29lbWJlZCA8LSBSdW5QQ0EoZHBxLmNvZW1iZWQsIGZlYXR1cmVzID0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uKSAtLT4KCjwhLS0gcGxvdF9ncmlkKCAtLT4KPCEtLSAgIERpbVBsb3QoZHBxLmNvZW1iZWQsIGdyb3VwLmJ5ID0gYygidGVjaCIpLCByZWR1Y3Rpb24gPSAicGNhIikgLCAtLT4KPCEtLSAgIEZlYXR1cmVQbG90KGRwcS5jb2VtYmVkLCBmZWF0dXJlPSJkcHRfcHNldWRvdGltZSIsIHJlZHVjdGlvbiA9ICJwY2EiKSArIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICAtLT4KPCEtLSAgICkgLS0+CjwhLS0gYGBgIC0tPgo8IS0tIGBgYHtyfSAtLT4KPCEtLSB0b3BQQzEuZGYgPC0gZHBxLmNvZW1iZWRAcmVkdWN0aW9ucyRwY2FAZmVhdHVyZS5sb2FkaW5nc1ssMSwgZHJvcD1GXSAlPiUgLS0+CjwhLS0gICBhcy50aWJibGUocm93bmFtZXM9ImdlbmUiKSAlPiUgLS0+CjwhLS0gICAjIGFycmFuZ2UoUENfMSkgLS0+CjwhLS0gICBtdXRhdGUocmFuaz0gZGVuc2VfcmFuayhQQ18xKSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGxhYmVsPWlmZWxzZShyYW5rID4gKG4oKS0xMCkgfCByYW5rIDwgKDEwKSAsIGdlbmUsTkEpKSAgLS0+Cgo8IS0tIHRvcFBDMS5kZiAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKHJhbmssIFBDXzEpKSArIC0tPgo8IS0tICAgZ2VvbV9wb2ludCgpKyAtLT4KPCEtLSAgIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhPS4gJT4lIGZpbHRlcighaXMubmEobGFiZWwpKSwgYWVzKGxhYmVsPWxhYmVsKSkgLS0+CjwhLS0gYGBgIC0tPgo8IS0tIGBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fSAtLT4KPCEtLSBGZWF0dXJlUGxvdChkcHEuY29lbWJlZCwgZmVhdHVyZT11bmlxdWUodG9wUEMxLmRmJGxhYmVsKVsxOjZdLCByZWR1Y3Rpb24gPSAidW1hcCIpICAtLT4KPCEtLSAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fSAtLT4KPCEtLSBjY2Eub2JqQHJlZHVjdGlvbnMkY2NhQGZlYXR1cmUubG9hZGluZ3MgJT4lIC0tPgo8IS0tICAgcmVzaGFwZTIgLS0+CjwhLS0gICBwaGVhdG1hcDo6cGhlYXRtYXAoKSAtLT4KPCEtLSBEaW1IZWF0bWFwKGNjYS5vYmosIHJlZHVjdGlvbiA9ICJjY2EiLCBhc3NheXMgPSAiQVRBQyIpIC0tPgo8IS0tIEZlYXR1cmVQbG90KGNjYS5vYmosIHJlZHVjdGlvbj0iY2NhIiwgZmVhdHVyZT1nZW5lLm9pKSAtLT4KCjwhLS0gYGBgIC0tPgoKCjwhLS0gPCEtLSBEaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgYmV0d2VlbiBSTkEgYW5kIEFUQUMgRFAoUSkgY2VsbHMgLS0+IC0tPgo8IS0tIDwhLS0gYGBge3J9IC0tPiAtLT4KPCEtLSA8IS0tIFZhcmlhYmxlRmVhdHVyZXMoY29lbWJlZCkgPC0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uIC0tPiAtLT4KPCEtLSA8IS0tIGRwcS5kaWZmIDwtIEZpbmRNYXJrZXJzKGRwcS5jb2VtYmVkLCBmZWF0dXJlcz1WYXJpYWJsZUZlYXR1cmVzKGNvZW1iZWQpLCAgLS0+IC0tPgo8IS0tIDwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAidGVjaCIsIGlkZW50LjEgPSAiUk5BIiwgaWRlbnQuMiA9ICJBVEFDIikgLS0+IC0tPgoKPCEtLSA8IS0tIHRvcC5kaWZmIDwtIHJvd25hbWVzKGRwcS5kaWZmWzE6MTAsXSkgLS0+IC0tPgo8IS0tIDwhLS0gRmVhdHVyZVBsb3QoY29lbWJlZCwgZmVhdHVyZXMgPSB0b3AuZGlmZlsxOjNdLCBzcGxpdC5ieSA9ICJ0ZWNoIiwgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMDApLCBzbG90ID0gInNjYWxlLmRhdGEiLCBtYXguY3V0b2ZmID0gMTApICsgZ2d0aXRsZSgiU3RhZ2UgTWFya2VycyIpIC0tPiAtLT4KPCEtLSA8IS0tIGBgYCAtLT4gLS0+CjwhLS0gPCEtLSBgYGB7cn0gLS0+IC0tPgoKPCEtLSA8IS0tIFZsblBsb3QoY29lbWJlZFssIGNvZW1iZWQkdGVjaD09IlJOQSJdLCBmZWF0dXJlcyA9IHRvcC5kaWZmWzFdLCBncm91cC5ieSA9ICJhbm5vdGF0aW9uIiwgcHQuc2l6ZSA9IDAuMSwgc3BsaXQuYnkgPSAndGVjaCcpIC0tPiAtLT4KPCEtLSA8IS0tIFZsblBsb3QoY29lbWJlZCwgZmVhdHVyZXMgPSBjKCJBUVAzIiwgIlRSQkMyIiksIGdyb3VwLmJ5ID0gInRlY2giKSAtLT4gLS0+CjwhLS0gPCEtLSBgYGAgLS0+IC0tPgoKPCEtLSBgYGB7ciwgZmlnLndpZHRoPTEyfSAtLT4KPCEtLSBhdGFjLm1hdCA8LSBjb2VtYmVkQGFzc2F5cyRBVEFDQGRhdGEgLS0+CjwhLS0gcm5hLm1hdCA8LSBjb2VtYmVkQGFzc2F5cyRSTkFAZGF0YSAtLT4KCjwhLS0gZ2VuZS5vaSA8LSB1bmlxdWUodG9wUEMxLmRmJGxhYmVsKSAlPiUgc3RyX3N1YnNldCgiVFJBViIpIC0tPgo8IS0tIGdlbmUub2kgLS0+CjwhLS0gYXRhYy5kZiA8LSBhdGFjLm1hdFtnZW5lLm9pLCwgZHJvcD1GXSAlPiUgLS0+CjwhLS0gICBhcy5tYXRyaXgoKSAlPiUgLS0+CjwhLS0gICByZXNoYXBlMjo6bWVsdCh2YXJuYW1lcz1jKCJnZW5lIiwgImNlbGwiKSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKHRlY2g9IkFUQUMiKSAlPiUgLS0+CjwhLS0gICBsZWZ0X2pvaW4oZHB0LmRmLCBieT1jKCdjZWxsJywgInRlY2giKSkgIC0tPgo8IS0tICMgJT4lIC0tPgo8IS0tICMgICBncm91cF9ieSh0ZWNoLCBkcHRfYmluLCBnZW5lKSAlPiUgLS0+CjwhLS0gIyAgIHN1bW1hcmlzZShmcmFjPXN1bSh2YWx1ZSE9MCkvbigpKSAtLT4KCjwhLS0gcm5hLmRmIDwtIHJuYS5tYXRbZ2VuZS5vaSwsIGRyb3A9Rl0gJT4lIC0tPgo8IS0tICAgYXMubWF0cml4KCkgJT4lIC0tPgo8IS0tICAgcmVzaGFwZTI6Om1lbHQodmFybmFtZXM9YygiZ2VuZSIsICJjZWxsIikpICU+JSAtLT4KPCEtLSAgIGZpbHRlcihzdHJfZGV0ZWN0KGNlbGwsICJSTkFfIikpICU+JSAtLT4KPCEtLSAgIG11dGF0ZSh0ZWNoPSJSTkEiKSAlPiUgLS0+CjwhLS0gICBsZWZ0X2pvaW4oZHB0LmRmLCBieT1jKCdjZWxsJywgInRlY2giKSkgLS0+CgoKPCEtLSBhY2MucGwgPC0gYXRhYy5kZiAlPiUgLS0+CjwhLS0gICBkcm9wX25hKGRwdF9iaW4pICU+JSAtLT4KPCEtLSAgIGdncGxvdChhZXMoZHB0X2JpbiwgZmlsbD1hcy5mYWN0b3IodmFsdWUpKSkgKyAtLT4KPCEtLSAgIGdlb21fYmFyKCkgIC0tPgo8IS0tICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwsIG5hLnZhbHVlPSJncmV5NTAiKSAtLT4KPCEtLSBhY2MucGwgLS0+CjwhLS0gZXgucGwgPC0gcm5hLmRmICU+JSAtLT4KPCEtLSAgIGZpbHRlcihnZW5lPT1nZW5lLm9pKSAlPiUgLS0+CjwhLS0gICAjIG11dGF0ZSh2YWx1ZT1pZmVsc2UodmFsdWU+MCwxLDApKSAlPiUgLS0+CjwhLS0gICAjIGZpbHRlcih2YWx1ZSE9MCkgJT4lIC0tPgo8IS0tICAgZ2dwbG90KGFlcyhkcHRfYmluLCB2YWx1ZSkpICsgLS0+CjwhLS0gICAjIGdlb21fdmlvbGluKGFscGhhPTAuMikgKyAtLT4KPCEtLSAgICMgZ2VvbV9wb2ludCgpIC0tPgo8IS0tICAgIyBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpIC0tPgo8IS0tICAgIyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y2VsbC50eXBlLnBhbCkgLS0+CjwhLS0gICBnZW9tX2ppdHRlcihhbHBoYT0wLjUsIHNpemU9MC41KSArIC0tPgo8IS0tICAgZ2VvbV9zbW9vdGgoKSAgLS0+CjwhLS0gICB4bGltKDAsNTApIC0tPgoKCjwhLS0gcGxvdF9ncmlkKGFjYy5wbCwgZXgucGwsIG5jb2w9MSwgbnJvdz0yLCBhbGlnbiA9ICJ2IiwgYXhpcy49ImwiKSAtLT4KPCEtLSBgYGAgLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBhdGFjLmRmICU+JSAtLT4KPCEtLSAgIGRyb3BfbmEoZHB0X3BzZXVkb3RpbWUpICU+JSAtLT4KPCEtLSAgIGdyb3VwX2J5KCBnZW5lLCBkcHRfYmluKSAlPiUgLS0+CjwhLS0gICAjIHN1bW1hcmlzZShmcmFjPXN1bSh2YWx1ZSkvbigpKSAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIHZhbHVlLCBjb2xvcj1nZW5lKSkgKyAtLT4KPCEtLSAgICMgZ2VvbV9wb2ludCgpICsgLS0+CjwhLS0gICBnZW9tX3Ntb290aCgpIC0tPgo8IS0tIGBgYCAtLT4KCi0tLQoKCgoKCgoK